summaryrefslogtreecommitdiffstats
path: root/drivers/usb/mtu3/mtu3_core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/mtu3/mtu3_core.c')
-rw-r--r--drivers/usb/mtu3/mtu3_core.c119
1 files changed, 103 insertions, 16 deletions
diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c
index 562f4357831e..c4a2c37abf62 100644
--- a/drivers/usb/mtu3/mtu3_core.c
+++ b/drivers/usb/mtu3/mtu3_core.c
@@ -141,6 +141,28 @@ static void mtu3_device_disable(struct mtu3 *mtu)
mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);
}
+static void mtu3_dev_power_on(struct mtu3 *mtu)
+{
+ void __iomem *ibase = mtu->ippc_base;
+
+ mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);
+ if (mtu->is_u3_ip)
+ mtu3_clrbits(ibase, SSUSB_U3_CTRL(0), SSUSB_U3_PORT_PDN);
+
+ mtu3_clrbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_PDN);
+}
+
+static void mtu3_dev_power_down(struct mtu3 *mtu)
+{
+ void __iomem *ibase = mtu->ippc_base;
+
+ if (mtu->is_u3_ip)
+ mtu3_setbits(ibase, SSUSB_U3_CTRL(0), SSUSB_U3_PORT_PDN);
+
+ mtu3_setbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_PDN);
+ mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);
+}
+
/* reset U3D's device module. */
static void mtu3_device_reset(struct mtu3 *mtu)
{
@@ -227,11 +249,13 @@ static void mtu3_set_speed(struct mtu3 *mtu, enum usb_device_speed speed)
mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE);
break;
case USB_SPEED_SUPER:
+ mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE);
mtu3_clrbits(mtu->ippc_base, SSUSB_U3_CTRL(0),
SSUSB_U3_PORT_SSP_SPEED);
break;
case USB_SPEED_SUPER_PLUS:
- mtu3_setbits(mtu->ippc_base, SSUSB_U3_CTRL(0),
+ mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE);
+ mtu3_setbits(mtu->ippc_base, SSUSB_U3_CTRL(0),
SSUSB_U3_PORT_SSP_SPEED);
break;
default:
@@ -333,12 +357,7 @@ void mtu3_start(struct mtu3 *mtu)
dev_dbg(mtu->dev, "%s devctl 0x%x\n", __func__,
mtu3_readl(mbase, U3D_DEVICE_CONTROL));
- mtu3_clrbits(mtu->ippc_base, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);
- if (mtu->is_u3_ip)
- mtu3_clrbits(mtu->ippc_base, SSUSB_U3_CTRL(0), SSUSB_U3_PORT_PDN);
-
- mtu3_clrbits(mtu->ippc_base, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_PDN);
-
+ mtu3_dev_power_on(mtu);
mtu3_csr_init(mtu);
mtu3_set_speed(mtu, mtu->speed);
@@ -360,12 +379,25 @@ void mtu3_stop(struct mtu3 *mtu)
mtu3_dev_on_off(mtu, 0);
mtu->is_active = 0;
+ mtu3_dev_power_down(mtu);
+}
- if (mtu->is_u3_ip)
- mtu3_setbits(mtu->ippc_base, SSUSB_U3_CTRL(0), SSUSB_U3_PORT_PDN);
+static void mtu3_dev_suspend(struct mtu3 *mtu)
+{
+ if (!mtu->is_active)
+ return;
- mtu3_setbits(mtu->ippc_base, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_PDN);
- mtu3_setbits(mtu->ippc_base, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);
+ 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 */
@@ -688,11 +720,15 @@ static irqreturn_t mtu3_link_isr(struct mtu3 *mtu)
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;
}
@@ -888,9 +924,16 @@ int ssusb_gadget_init(struct ssusb_mtk *ssusb)
if (mtu == NULL)
return -ENOMEM;
- mtu->irq = platform_get_irq(pdev, 0);
- if (mtu->irq < 0)
- return mtu->irq;
+ mtu->irq = platform_get_irq_byname_optional(pdev, "device");
+ if (mtu->irq < 0) {
+ if (mtu->irq == -EPROBE_DEFER)
+ return mtu->irq;
+
+ /* for backward compatibility */
+ mtu->irq = platform_get_irq(pdev, 0);
+ if (mtu->irq < 0)
+ return mtu->irq;
+ }
dev_info(dev, "irq %d\n", mtu->irq);
mtu->mac_base = devm_platform_ioremap_resource_byname(pdev, "mac");
@@ -965,3 +1008,47 @@ void ssusb_gadget_exit(struct ssusb_mtk *ssusb)
device_init_wakeup(ssusb->dev, false);
mtu3_hw_exit(mtu);
}
+
+bool ssusb_gadget_ip_sleep_check(struct ssusb_mtk *ssusb)
+{
+ struct mtu3 *mtu = ssusb->u3d;
+
+ /* host only, should wait for ip sleep */
+ if (!mtu)
+ return true;
+
+ /* device is started and pullup D+, ip can sleep */
+ if (mtu->is_active && mtu->softconnect)
+ return true;
+
+ /* ip can't sleep if not pullup D+ when support device mode */
+ return false;
+}
+
+int ssusb_gadget_suspend(struct ssusb_mtk *ssusb, pm_message_t msg)
+{
+ struct mtu3 *mtu = ssusb->u3d;
+
+ if (!mtu->gadget_driver)
+ return 0;
+
+ if (mtu->connected)
+ return -EBUSY;
+
+ mtu3_dev_suspend(mtu);
+ synchronize_irq(mtu->irq);
+
+ return 0;
+}
+
+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;
+}