unsigned is_u3_ip:1;
unsigned delayed_status:1;
unsigned gen2cp:1;
+ unsigned connected:1;
u8 address;
u8 test_mode_nr;
*/
#include <linux/dma-mapping.h>
+#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_address.h>
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 */
int mtu3_config_ep(struct mtu3 *mtu, struct mtu3_ep *mep,
int interval, int burst, int mult)
mtu->g.speed = udev_speed;
mtu->g.ep0->maxpacket = maxpkt;
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);
- else
+ pm_runtime_put(mtu->dev);
+ } else {
+ pm_runtime_get(mtu->dev);
mtu3_ep0_setup(mtu);
+ }
return IRQ_HANDLED;
}
device_init_wakeup(ssusb->dev, false);
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;
+}
#if IS_ENABLED(CONFIG_USB_MTU3_GADGET) || IS_ENABLED(CONFIG_USB_MTU3_DUAL_ROLE)
int ssusb_gadget_init(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
static inline int ssusb_gadget_init(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
dev_dbg(mtu->dev, "%s (%s) for %sactive device\n", __func__,
is_on ? "on" : "off", mtu->is_active ? "" : "in");
+ pm_runtime_get_sync(mtu->dev);
+
/* we'd rather not pullup unless the device is active. */
spin_lock_irqsave(&mtu->lock, flags);
}
spin_unlock_irqrestore(&mtu->lock, flags);
+ pm_runtime_put(mtu->dev);
return 0;
}
}
dev_dbg(mtu->dev, "bind driver %s\n", driver->function);
+ pm_runtime_get_sync(mtu->dev);
spin_lock_irqsave(&mtu->lock, flags);
mtu3_start(mtu);
spin_unlock_irqrestore(&mtu->lock, flags);
+ pm_runtime_put(mtu->dev);
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)
{
struct ssusb_mtk *ssusb = dev_get_drvdata(dev);
+ int ret = 0;
dev_dbg(dev, "%s\n", __func__);
- /* REVISIT: disconnect it for only device mode? */
- if (!ssusb->is_host)
- return 0;
+ switch (ssusb->dr_mode) {
+ case USB_DR_MODE_PERIPHERAL:
+ 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);
clk_bulk_disable_unprepare(BULK_CLKS_CNT, ssusb->clks);
ssusb_wakeup_set(ssusb, true);
dev_dbg(dev, "%s\n", __func__);
- if (!ssusb->is_host)
- return 0;
-
ssusb_wakeup_set(ssusb, false);
ret = clk_bulk_prepare_enable(BULK_CLKS_CNT, ssusb->clks);
if (ret)
if (ret)
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;