Commit 427c6642 authored by Chunfeng Yun's avatar Chunfeng Yun Committed by Greg Kroah-Hartman

usb: mtu3: support suspend/resume for device mode

Support suspend/resume for device mode if the device is not
connected with a host, otherwise reject to suspend.
Signed-off-by: default avatarChunfeng Yun <chunfeng.yun@mediatek.com>
Link: https://lore.kernel.org/r/1626340078-29111-13-git-send-email-chunfeng.yun@mediatek.comSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 62448315
...@@ -356,6 +356,7 @@ struct mtu3 { ...@@ -356,6 +356,7 @@ struct mtu3 {
unsigned is_u3_ip:1; unsigned is_u3_ip:1;
unsigned delayed_status:1; unsigned delayed_status:1;
unsigned gen2cp:1; unsigned gen2cp:1;
unsigned connected:1;
u8 address; u8 address;
u8 test_mode_nr; u8 test_mode_nr;
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
*/ */
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/iopoll.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_address.h> #include <linux/of_address.h>
...@@ -380,6 +381,24 @@ void mtu3_stop(struct mtu3 *mtu) ...@@ -380,6 +381,24 @@ void mtu3_stop(struct mtu3 *mtu)
mtu3_dev_power_down(mtu); mtu3_dev_power_down(mtu);
} }
static void mtu3_dev_suspend(struct mtu3 *mtu)
{
if (!mtu->is_active)
return;
mtu3_intr_disable(mtu);
mtu3_dev_power_down(mtu);
}
static void mtu3_dev_resume(struct mtu3 *mtu)
{
if (!mtu->is_active)
return;
mtu3_dev_power_on(mtu);
mtu3_intr_enable(mtu);
}
/* for non-ep0 */ /* for non-ep0 */
int mtu3_config_ep(struct mtu3 *mtu, struct mtu3_ep *mep, int mtu3_config_ep(struct mtu3 *mtu, struct mtu3_ep *mep,
int interval, int burst, int mult) int interval, int burst, int mult)
...@@ -700,11 +719,15 @@ static irqreturn_t mtu3_link_isr(struct mtu3 *mtu) ...@@ -700,11 +719,15 @@ static irqreturn_t mtu3_link_isr(struct mtu3 *mtu)
mtu->g.speed = udev_speed; mtu->g.speed = udev_speed;
mtu->g.ep0->maxpacket = maxpkt; mtu->g.ep0->maxpacket = maxpkt;
mtu->ep0_state = MU3D_EP0_STATE_SETUP; mtu->ep0_state = MU3D_EP0_STATE_SETUP;
mtu->connected = !!(udev_speed != USB_SPEED_UNKNOWN);
if (udev_speed == USB_SPEED_UNKNOWN) if (udev_speed == USB_SPEED_UNKNOWN) {
mtu3_gadget_disconnect(mtu); mtu3_gadget_disconnect(mtu);
else pm_runtime_put(mtu->dev);
} else {
pm_runtime_get(mtu->dev);
mtu3_ep0_setup(mtu); mtu3_ep0_setup(mtu);
}
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -984,3 +1007,44 @@ void ssusb_gadget_exit(struct ssusb_mtk *ssusb) ...@@ -984,3 +1007,44 @@ void ssusb_gadget_exit(struct ssusb_mtk *ssusb)
device_init_wakeup(ssusb->dev, false); device_init_wakeup(ssusb->dev, false);
mtu3_hw_exit(mtu); mtu3_hw_exit(mtu);
} }
int ssusb_gadget_suspend(struct ssusb_mtk *ssusb, pm_message_t msg)
{
struct mtu3 *mtu = ssusb->u3d;
void __iomem *ibase = mtu->ippc_base;
u32 value;
int ret = 0;
if (!mtu->gadget_driver)
return 0;
if (mtu->connected)
return -EBUSY;
mtu3_dev_suspend(mtu);
synchronize_irq(mtu->irq);
/* wait for ip to sleep */
if (mtu->is_active && mtu->softconnect) {
ret = readl_poll_timeout(ibase + U3D_SSUSB_IP_PW_STS1,
value, (value & SSUSB_IP_SLEEP_STS), 100, 100000);
if (ret) {
dev_err(mtu->dev, "ip sleep failed!!!\n");
ret = -EBUSY;
}
}
return ret;
}
int ssusb_gadget_resume(struct ssusb_mtk *ssusb, pm_message_t msg)
{
struct mtu3 *mtu = ssusb->u3d;
if (!mtu->gadget_driver)
return 0;
mtu3_dev_resume(mtu);
return 0;
}
...@@ -57,6 +57,8 @@ static inline void ssusb_wakeup_set(struct ssusb_mtk *ssusb, bool enable) ...@@ -57,6 +57,8 @@ static inline void ssusb_wakeup_set(struct ssusb_mtk *ssusb, bool enable)
#if IS_ENABLED(CONFIG_USB_MTU3_GADGET) || IS_ENABLED(CONFIG_USB_MTU3_DUAL_ROLE) #if IS_ENABLED(CONFIG_USB_MTU3_GADGET) || IS_ENABLED(CONFIG_USB_MTU3_DUAL_ROLE)
int ssusb_gadget_init(struct ssusb_mtk *ssusb); int ssusb_gadget_init(struct ssusb_mtk *ssusb);
void ssusb_gadget_exit(struct ssusb_mtk *ssusb); void ssusb_gadget_exit(struct ssusb_mtk *ssusb);
int ssusb_gadget_suspend(struct ssusb_mtk *ssusb, pm_message_t msg);
int ssusb_gadget_resume(struct ssusb_mtk *ssusb, pm_message_t msg);
#else #else
static inline int ssusb_gadget_init(struct ssusb_mtk *ssusb) static inline int ssusb_gadget_init(struct ssusb_mtk *ssusb)
{ {
...@@ -65,6 +67,18 @@ static inline int ssusb_gadget_init(struct ssusb_mtk *ssusb) ...@@ -65,6 +67,18 @@ static inline int ssusb_gadget_init(struct ssusb_mtk *ssusb)
static inline void ssusb_gadget_exit(struct ssusb_mtk *ssusb) static inline void ssusb_gadget_exit(struct ssusb_mtk *ssusb)
{} {}
static inline int
ssusb_gadget_suspend(struct ssusb_mtk *ssusb, pm_message_t msg)
{
return 0;
}
static inline int
ssusb_gadget_resume(struct ssusb_mtk *ssusb, pm_message_t msg)
{
return 0;
}
#endif #endif
......
...@@ -469,6 +469,8 @@ static int mtu3_gadget_pullup(struct usb_gadget *gadget, int is_on) ...@@ -469,6 +469,8 @@ static int mtu3_gadget_pullup(struct usb_gadget *gadget, int is_on)
dev_dbg(mtu->dev, "%s (%s) for %sactive device\n", __func__, dev_dbg(mtu->dev, "%s (%s) for %sactive device\n", __func__,
is_on ? "on" : "off", mtu->is_active ? "" : "in"); is_on ? "on" : "off", mtu->is_active ? "" : "in");
pm_runtime_get_sync(mtu->dev);
/* we'd rather not pullup unless the device is active. */ /* we'd rather not pullup unless the device is active. */
spin_lock_irqsave(&mtu->lock, flags); spin_lock_irqsave(&mtu->lock, flags);
...@@ -482,6 +484,7 @@ static int mtu3_gadget_pullup(struct usb_gadget *gadget, int is_on) ...@@ -482,6 +484,7 @@ static int mtu3_gadget_pullup(struct usb_gadget *gadget, int is_on)
} }
spin_unlock_irqrestore(&mtu->lock, flags); spin_unlock_irqrestore(&mtu->lock, flags);
pm_runtime_put(mtu->dev);
return 0; return 0;
} }
...@@ -499,6 +502,7 @@ static int mtu3_gadget_start(struct usb_gadget *gadget, ...@@ -499,6 +502,7 @@ static int mtu3_gadget_start(struct usb_gadget *gadget,
} }
dev_dbg(mtu->dev, "bind driver %s\n", driver->function); dev_dbg(mtu->dev, "bind driver %s\n", driver->function);
pm_runtime_get_sync(mtu->dev);
spin_lock_irqsave(&mtu->lock, flags); spin_lock_irqsave(&mtu->lock, flags);
...@@ -509,6 +513,7 @@ static int mtu3_gadget_start(struct usb_gadget *gadget, ...@@ -509,6 +513,7 @@ static int mtu3_gadget_start(struct usb_gadget *gadget,
mtu3_start(mtu); mtu3_start(mtu);
spin_unlock_irqrestore(&mtu->lock, flags); spin_unlock_irqrestore(&mtu->lock, flags);
pm_runtime_put(mtu->dev);
return 0; return 0;
} }
......
...@@ -421,21 +421,32 @@ static int mtu3_remove(struct platform_device *pdev) ...@@ -421,21 +421,32 @@ static int mtu3_remove(struct platform_device *pdev)
return 0; return 0;
} }
/*
* when support dual-role mode, we reject suspend when
* it works as device mode;
*/
static int mtu3_suspend_common(struct device *dev, pm_message_t msg) static int mtu3_suspend_common(struct device *dev, pm_message_t msg)
{ {
struct ssusb_mtk *ssusb = dev_get_drvdata(dev); struct ssusb_mtk *ssusb = dev_get_drvdata(dev);
int ret = 0;
dev_dbg(dev, "%s\n", __func__); dev_dbg(dev, "%s\n", __func__);
/* REVISIT: disconnect it for only device mode? */ switch (ssusb->dr_mode) {
if (!ssusb->is_host) case USB_DR_MODE_PERIPHERAL:
return 0; ret = ssusb_gadget_suspend(ssusb, msg);
if (ret)
return ret;
ssusb_host_suspend(ssusb); break;
case USB_DR_MODE_HOST:
ssusb_host_suspend(ssusb);
break;
case USB_DR_MODE_OTG:
if (!ssusb->is_host)
return 0;
ssusb_host_suspend(ssusb);
break;
default:
return -EINVAL;
}
ssusb_phy_power_off(ssusb); ssusb_phy_power_off(ssusb);
clk_bulk_disable_unprepare(BULK_CLKS_CNT, ssusb->clks); clk_bulk_disable_unprepare(BULK_CLKS_CNT, ssusb->clks);
ssusb_wakeup_set(ssusb, true); ssusb_wakeup_set(ssusb, true);
...@@ -450,9 +461,6 @@ static int mtu3_resume_common(struct device *dev, pm_message_t msg) ...@@ -450,9 +461,6 @@ static int mtu3_resume_common(struct device *dev, pm_message_t msg)
dev_dbg(dev, "%s\n", __func__); dev_dbg(dev, "%s\n", __func__);
if (!ssusb->is_host)
return 0;
ssusb_wakeup_set(ssusb, false); ssusb_wakeup_set(ssusb, false);
ret = clk_bulk_prepare_enable(BULK_CLKS_CNT, ssusb->clks); ret = clk_bulk_prepare_enable(BULK_CLKS_CNT, ssusb->clks);
if (ret) if (ret)
...@@ -462,7 +470,22 @@ static int mtu3_resume_common(struct device *dev, pm_message_t msg) ...@@ -462,7 +470,22 @@ static int mtu3_resume_common(struct device *dev, pm_message_t msg)
if (ret) if (ret)
goto phy_err; goto phy_err;
ssusb_host_resume(ssusb, false); switch (ssusb->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
ssusb_gadget_resume(ssusb, msg);
break;
case USB_DR_MODE_HOST:
ssusb_host_resume(ssusb, false);
break;
case USB_DR_MODE_OTG:
if (!ssusb->is_host)
return 0;
ssusb_host_resume(ssusb, true);
break;
default:
return -EINVAL;
}
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment