2021-07-15 09:10:19

by Chunfeng Yun

[permalink] [raw]
Subject: [PATCH v3 00/13] Add support mtu3 gadget (runtime) PM

This series mainly adds support for mtu3 gadget suspend/resume when
the controller works at device only mode or dual role mode, and also
adds support runtime PM.

v3:
add Acked-by Rob
fix use-after-free issue when probe failed
v2:
Change the comment of cover letter and its title.

In order to help review, v2 drops the patches about role-switch
rebuilding which are applied, and the left ones are identical.


Chunfeng Yun (13):
dt-bindings: usb: mtu3: remove support VBUS detection of extcon
dt-bindings: usb: mtu3: add optional property to disable usb2 ports
dt-bindings: usb: mtu3: add support property role-switch-default-mode
dt-bindings: usb: mtu3: add wakeup interrupt
usb: common: add helper to get role-switch-default-mode
usb: dwc3: drd: use helper to get role-switch-default-mode
usb: mtu3: support property role-switch-default-mode
usb: mtu3: support option to disable usb2 ports
usb: mtu3: add new helpers for host suspend/resume
usb: mtu3: support runtime PM for host mode
usb: mtu3: add helper to power on/down device
usb: mtu3: support suspend/resume for device mode
usb: mtu3: support suspend/resume for dual-role mode

.../bindings/usb/mediatek,mtu3.yaml | 47 ++++-
drivers/usb/common/common.c | 20 +++
drivers/usb/dwc3/drd.c | 8 +-
drivers/usb/mtu3/mtu3.h | 8 +
drivers/usb/mtu3/mtu3_core.c | 115 ++++++++++--
drivers/usb/mtu3/mtu3_dr.c | 26 ++-
drivers/usb/mtu3/mtu3_dr.h | 30 +++-
drivers/usb/mtu3/mtu3_gadget.c | 5 +
drivers/usb/mtu3/mtu3_host.c | 106 +++++++++--
drivers/usb/mtu3/mtu3_plat.c | 166 +++++++++++++++---
include/linux/usb/otg.h | 1 +
11 files changed, 457 insertions(+), 75 deletions(-)

--
2.18.0


2021-07-15 09:10:24

by Chunfeng Yun

[permalink] [raw]
Subject: [PATCH v3 01/13] dt-bindings: usb: mtu3: remove support VBUS detection of extcon

Due to no platforms use a multi-funtion pin to detect VBUS
status until now when support dual role mode, prefer not to
support it anymore;
Another reason is that the controller doesn't support idle status,
the dual role port works as device or as host, using an IDDIG pin
is good enough, this will help to save another multi-function pin;
And fix a typo of 'neede';
Last modify example using VBUS to turn on/off device, for device
only mode, the driver doesn't get extcon.

Acked-by: Rob Herring <[email protected]>
Signed-off-by: Chunfeng Yun <[email protected]>
---
v3: add Acked-by Rob
v2: no changes
---
.../bindings/usb/mediatek,mtu3.yaml | 22 +++++++++++++++----
1 file changed, 18 insertions(+), 4 deletions(-)

diff --git a/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml b/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml
index dbc7876e0a0b..2cac7a87ce36 100644
--- a/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml
+++ b/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml
@@ -106,7 +106,7 @@ properties:
extcon:
deprecated: true
description: |
- Phandle to the extcon device detecting the IDDIG/VBUS state, neede
+ Phandle to the extcon device detecting the IDDIG state, needed
when supports dual-role mode.
It's considered valid for compatibility reasons, not allowed for
new bindings, and use "usb-role-switch" property instead.
@@ -230,7 +230,7 @@ examples:
};
};

- # Enable/disable device by an input gpio for VBUS pin
+ # Dual role switch by gpio-usb-b-connector
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/power/mt2712-power.h>
@@ -244,13 +244,27 @@ examples:
power-domains = <&scpsys MT2712_POWER_DOMAIN_USB2>;
clocks = <&topckgen CLK_TOP_USB30_SEL>;
clock-names = "sys_ck";
- dr_mode = "peripheral";
+ dr_mode = "otg";
usb-role-switch;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ host0: usb@11270000 {
+ compatible = "mediatek,mt2712-xhci", "mediatek,mtk-xhci";
+ reg = <0x11270000 0x1000>;
+ reg-names = "mac";
+ interrupts = <GIC_SPI 123 IRQ_TYPE_LEVEL_LOW>;
+ power-domains = <&scpsys MT2712_POWER_DOMAIN_USB>;
+ clocks = <&topckgen CLK_TOP_USB30_SEL>, <&clk26m>;
+ clock-names = "sys_ck", "ref_ck";
+ };

connector {
compatible = "gpio-usb-b-connector", "usb-b-connector";
type = "micro";
- vbus-gpios = <&pio 13 GPIO_ACTIVE_HIGH>;
+ id-gpios = <&pio 12 GPIO_ACTIVE_HIGH>;
+ vbus-supply = <&usb_p0_vbus>;
};
};

--
2.18.0

2021-07-15 09:10:24

by Chunfeng Yun

[permalink] [raw]
Subject: [PATCH v3 02/13] dt-bindings: usb: mtu3: add optional property to disable usb2 ports

Add support to disable specific usb2 host ports, it's useful when
a usb2 port is disabled on some platforms, but enabled on others for
the same SoC, another case is that the different package may support
different number of ports.

Signed-off-by: Chunfeng Yun <[email protected]>
---
Due to there is property 'mediatek,u3p-dis-msk', prefer to use
a similar name for the similar opertions, not spell out 'mask'
as suggested by Rob.

v2~3: no changes
---
Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml b/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml
index 2cac7a87ce36..3e6f2750f48d 100644
--- a/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml
+++ b/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml
@@ -166,6 +166,12 @@ properties:
description: The mask to disable u3ports, bit0 for u3port0,
bit1 for u3port1, ... etc

+ mediatek,u2p-dis-msk:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: The mask to disable u2ports, bit0 for u2port0,
+ bit1 for u2port1, ... etc; but can't disable u2port0 if dual role mode
+ is enabled, so will be skipped in this case.
+
# Required child node when support dual-role
patternProperties:
"^usb@[0-9a-f]+$":
--
2.18.0

2021-07-15 09:10:50

by Chunfeng Yun

[permalink] [raw]
Subject: [PATCH v3 07/13] usb: mtu3: support property role-switch-default-mode

Support default mode config when use usb-role-switch

Signed-off-by: Chunfeng Yun <[email protected]>
---
v2~3: no changes
---
drivers/usb/mtu3/mtu3.h | 2 ++
drivers/usb/mtu3/mtu3_dr.c | 24 ++++++++++++++++++++----
2 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h
index 5546b868b08b..bc82c7f97ad6 100644
--- a/drivers/usb/mtu3/mtu3.h
+++ b/drivers/usb/mtu3/mtu3.h
@@ -199,6 +199,7 @@ struct mtu3_gpd_ring {
* @id_nb : notifier for iddig(idpin) detection
* @dr_work : work for drd mode switch, used to avoid sleep in atomic context
* @desired_role : role desired to switch
+* @default_role : default mode while usb role is USB_ROLE_NONE
* @role_sw : use USB Role Switch to support dual-role switch, can't use
* extcon at the same time, and extcon is deprecated.
* @role_sw_used : true when the USB Role Switch is used.
@@ -212,6 +213,7 @@ struct otg_switch_mtk {
struct notifier_block id_nb;
struct work_struct dr_work;
enum usb_role desired_role;
+ enum usb_role default_role;
struct usb_role_switch *role_sw;
bool role_sw_used;
bool is_u3_drd;
diff --git a/drivers/usb/mtu3/mtu3_dr.c b/drivers/usb/mtu3/mtu3_dr.c
index 318fbc618137..30e7e5fc0f88 100644
--- a/drivers/usb/mtu3/mtu3_dr.c
+++ b/drivers/usb/mtu3/mtu3_dr.c
@@ -137,8 +137,12 @@ static void ssusb_mode_sw_work(struct work_struct *work)

current_role = ssusb->is_host ? USB_ROLE_HOST : USB_ROLE_DEVICE;

- if (desired_role == USB_ROLE_NONE)
+ if (desired_role == USB_ROLE_NONE) {
+ /* the default mode is host as probe does */
desired_role = USB_ROLE_HOST;
+ if (otg_sx->default_role == USB_ROLE_DEVICE)
+ desired_role = USB_ROLE_DEVICE;
+ }

if (current_role == desired_role)
return;
@@ -274,17 +278,29 @@ static int ssusb_role_sw_register(struct otg_switch_mtk *otg_sx)
{
struct usb_role_switch_desc role_sx_desc = { 0 };
struct ssusb_mtk *ssusb = otg_sx_to_ssusb(otg_sx);
+ struct device *dev = ssusb->dev;
+ enum usb_dr_mode mode;

if (!otg_sx->role_sw_used)
return 0;

+ mode = usb_get_role_switch_default_mode(dev);
+ if (mode == USB_DR_MODE_PERIPHERAL)
+ otg_sx->default_role = USB_ROLE_DEVICE;
+ else
+ otg_sx->default_role = USB_ROLE_HOST;
+
role_sx_desc.set = ssusb_role_sw_set;
role_sx_desc.get = ssusb_role_sw_get;
- role_sx_desc.fwnode = dev_fwnode(ssusb->dev);
+ role_sx_desc.fwnode = dev_fwnode(dev);
role_sx_desc.driver_data = ssusb;
- otg_sx->role_sw = usb_role_switch_register(ssusb->dev, &role_sx_desc);
+ otg_sx->role_sw = usb_role_switch_register(dev, &role_sx_desc);
+ if (IS_ERR(otg_sx->role_sw))
+ return PTR_ERR(otg_sx->role_sw);

- return PTR_ERR_OR_ZERO(otg_sx->role_sw);
+ ssusb_set_mode(otg_sx, otg_sx->default_role);
+
+ return 0;
}

int ssusb_otg_switch_init(struct ssusb_mtk *ssusb)
--
2.18.0

2021-07-15 09:11:01

by Chunfeng Yun

[permalink] [raw]
Subject: [PATCH v3 10/13] usb: mtu3: support runtime PM for host mode

Use a dedicated wakeup irq for runtime suspend/resume, and interrupts
names are provided if using wakeup irq, this patch only support host
mode.

Signed-off-by: Chunfeng Yun <[email protected]>
---
v3: fix free-after-use issue
v2: no changes
---
drivers/usb/mtu3/mtu3.h | 1 +
drivers/usb/mtu3/mtu3_core.c | 13 ++++++--
drivers/usb/mtu3/mtu3_plat.c | 64 ++++++++++++++++++++++++++++++++----
3 files changed, 69 insertions(+), 9 deletions(-)

diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h
index 0ae9b33b50ea..171e5b383063 100644
--- a/drivers/usb/mtu3/mtu3.h
+++ b/drivers/usb/mtu3/mtu3.h
@@ -246,6 +246,7 @@ struct ssusb_mtk {
void __iomem *ippc_base;
struct phy **phys;
int num_phys;
+ int wakeup_irq;
/* common power & clock */
struct regulator *vusb33;
struct clk_bulk_data clks[BULK_CLKS_CNT];
diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c
index 562f4357831e..6d23acb4fffc 100644
--- a/drivers/usb/mtu3/mtu3_core.c
+++ b/drivers/usb/mtu3/mtu3_core.c
@@ -888,9 +888,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");
diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c
index a906b24723e6..2be890f84c94 100644
--- a/drivers/usb/mtu3/mtu3_plat.c
+++ b/drivers/usb/mtu3/mtu3_plat.c
@@ -12,6 +12,7 @@
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
+#include <linux/pm_wakeirq.h>

#include "mtu3.h"
#include "mtu3_dr.h"
@@ -208,6 +209,10 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb)
if (IS_ERR(ssusb->ippc_base))
return PTR_ERR(ssusb->ippc_base);

+ ssusb->wakeup_irq = platform_get_irq_byname_optional(pdev, "wakeup");
+ if (ssusb->wakeup_irq == -EPROBE_DEFER)
+ return ssusb->wakeup_irq;
+
ssusb->dr_mode = usb_get_dr_mode(dev);
if (ssusb->dr_mode == USB_DR_MODE_UNKNOWN)
ssusb->dr_mode = USB_DR_MODE_OTG;
@@ -295,14 +300,25 @@ static int mtu3_probe(struct platform_device *pdev)
ssusb_debugfs_create_root(ssusb);

/* enable power domain */
+ pm_runtime_set_active(dev);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_autosuspend_delay(dev, 4000);
pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
- device_enable_async_suspend(dev);

ret = ssusb_rscs_init(ssusb);
if (ret)
goto comm_init_err;

+ if (ssusb->wakeup_irq > 0) {
+ ret = dev_pm_set_dedicated_wake_irq(dev, ssusb->wakeup_irq);
+ if (ret) {
+ dev_err(dev, "failed to set wakeup irq %d\n", ssusb->wakeup_irq);
+ goto comm_exit;
+ }
+ dev_info(dev, "wakeup irq %d\n", ssusb->wakeup_irq);
+ }
+
ssusb_ip_sw_reset(ssusb);

if (IS_ENABLED(CONFIG_USB_MTU3_HOST))
@@ -353,6 +369,11 @@ static int mtu3_probe(struct platform_device *pdev)
goto comm_exit;
}

+ device_enable_async_suspend(dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+ pm_runtime_forbid(dev);
+
return 0;

host_exit:
@@ -362,7 +383,7 @@ static int mtu3_probe(struct platform_device *pdev)
comm_exit:
ssusb_rscs_exit(ssusb);
comm_init_err:
- pm_runtime_put_sync(dev);
+ pm_runtime_put_noidle(dev);
pm_runtime_disable(dev);
ssusb_debugfs_remove_root(ssusb);

@@ -373,6 +394,8 @@ static int mtu3_remove(struct platform_device *pdev)
{
struct ssusb_mtk *ssusb = platform_get_drvdata(pdev);

+ pm_runtime_get_sync(&pdev->dev);
+
switch (ssusb->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
ssusb_gadget_exit(ssusb);
@@ -390,9 +413,10 @@ static int mtu3_remove(struct platform_device *pdev)
}

ssusb_rscs_exit(ssusb);
- pm_runtime_put_sync(&pdev->dev);
- pm_runtime_disable(&pdev->dev);
ssusb_debugfs_remove_root(ssusb);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);

return 0;
}
@@ -401,7 +425,7 @@ static int mtu3_remove(struct platform_device *pdev)
* when support dual-role mode, we reject suspend when
* it works as device mode;
*/
-static int __maybe_unused mtu3_suspend(struct device *dev)
+static int mtu3_suspend_common(struct device *dev, pm_message_t msg)
{
struct ssusb_mtk *ssusb = dev_get_drvdata(dev);

@@ -419,7 +443,7 @@ static int __maybe_unused mtu3_suspend(struct device *dev)
return 0;
}

-static int __maybe_unused mtu3_resume(struct device *dev)
+static int mtu3_resume_common(struct device *dev, pm_message_t msg)
{
struct ssusb_mtk *ssusb = dev_get_drvdata(dev);
int ret;
@@ -448,8 +472,36 @@ static int __maybe_unused mtu3_resume(struct device *dev)
return ret;
}

+static int __maybe_unused mtu3_suspend(struct device *dev)
+{
+ return mtu3_suspend_common(dev, PMSG_SUSPEND);
+}
+
+static int __maybe_unused mtu3_resume(struct device *dev)
+{
+ return mtu3_resume_common(dev, PMSG_SUSPEND);
+}
+
+static int __maybe_unused mtu3_runtime_suspend(struct device *dev)
+{
+ if (!device_may_wakeup(dev))
+ return 0;
+
+ return mtu3_suspend_common(dev, PMSG_AUTO_SUSPEND);
+}
+
+static int __maybe_unused mtu3_runtime_resume(struct device *dev)
+{
+ if (!device_may_wakeup(dev))
+ return 0;
+
+ return mtu3_resume_common(dev, PMSG_AUTO_SUSPEND);
+}
+
static const struct dev_pm_ops mtu3_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(mtu3_suspend, mtu3_resume)
+ SET_RUNTIME_PM_OPS(mtu3_runtime_suspend,
+ mtu3_runtime_resume, NULL)
};

#define DEV_PM_OPS (IS_ENABLED(CONFIG_PM) ? &mtu3_pm_ops : NULL)
--
2.18.0

2021-07-15 09:11:16

by Chunfeng Yun

[permalink] [raw]
Subject: [PATCH v3 09/13] usb: mtu3: add new helpers for host suspend/resume

Extract two helpers for host suspend and resume, will make it easy
to support dual-role mode suspend/resume later.

Signed-off-by: Chunfeng Yun <[email protected]>
---
v2~3: no changes
---
drivers/usb/mtu3/mtu3_dr.h | 8 ++--
drivers/usb/mtu3/mtu3_host.c | 92 ++++++++++++++++++++++++++++++++----
drivers/usb/mtu3/mtu3_plat.c | 4 +-
3 files changed, 88 insertions(+), 16 deletions(-)

diff --git a/drivers/usb/mtu3/mtu3_dr.h b/drivers/usb/mtu3/mtu3_dr.h
index 760fe7d69c6b..70dbf4706138 100644
--- a/drivers/usb/mtu3/mtu3_dr.h
+++ b/drivers/usb/mtu3/mtu3_dr.h
@@ -16,8 +16,8 @@ int ssusb_host_init(struct ssusb_mtk *ssusb, struct device_node *parent_dn);
void ssusb_host_exit(struct ssusb_mtk *ssusb);
int ssusb_wakeup_of_property_parse(struct ssusb_mtk *ssusb,
struct device_node *dn);
-int ssusb_host_enable(struct ssusb_mtk *ssusb);
-int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend);
+int ssusb_host_resume(struct ssusb_mtk *ssusb, bool p0_skipped);
+int ssusb_host_suspend(struct ssusb_mtk *ssusb);
void ssusb_wakeup_set(struct ssusb_mtk *ssusb, bool enable);

#else
@@ -38,12 +38,12 @@ static inline int ssusb_wakeup_of_property_parse(
return 0;
}

-static inline int ssusb_host_enable(struct ssusb_mtk *ssusb)
+static inline int ssusb_host_resume(struct ssusb_mtk *ssusb, bool p0_skipped)
{
return 0;
}

-static inline int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend)
+static inline int ssusb_host_suspend(struct ssusb_mtk *ssusb)
{
return 0;
}
diff --git a/drivers/usb/mtu3/mtu3_host.c b/drivers/usb/mtu3/mtu3_host.c
index 6185bac5fecf..a0a6a181b752 100644
--- a/drivers/usb/mtu3/mtu3_host.c
+++ b/drivers/usb/mtu3/mtu3_host.c
@@ -126,7 +126,7 @@ static void host_ports_num_get(struct ssusb_mtk *ssusb)
}

/* only configure ports will be used later */
-int ssusb_host_enable(struct ssusb_mtk *ssusb)
+static int ssusb_host_enable(struct ssusb_mtk *ssusb)
{
void __iomem *ibase = ssusb->ippc_base;
int num_u3p = ssusb->u3_ports;
@@ -171,13 +171,12 @@ int ssusb_host_enable(struct ssusb_mtk *ssusb)
return ssusb_check_clocks(ssusb, check_clk);
}

-int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend)
+static int ssusb_host_disable(struct ssusb_mtk *ssusb)
{
void __iomem *ibase = ssusb->ippc_base;
int num_u3p = ssusb->u3_ports;
int num_u2p = ssusb->u2_ports;
u32 value;
- int ret;
int i;

/* power down and disable u3 ports except skipped ones */
@@ -186,8 +185,7 @@ int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend)
continue;

value = mtu3_readl(ibase, SSUSB_U3_CTRL(i));
- value |= SSUSB_U3_PORT_PDN;
- value |= suspend ? 0 : SSUSB_U3_PORT_DIS;
+ value |= SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS;
mtu3_writel(ibase, SSUSB_U3_CTRL(i), value);
}

@@ -197,16 +195,90 @@ int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend)
continue;

value = mtu3_readl(ibase, SSUSB_U2_CTRL(i));
- value |= SSUSB_U2_PORT_PDN;
- value |= suspend ? 0 : SSUSB_U2_PORT_DIS;
+ value |= SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS;
mtu3_writel(ibase, SSUSB_U2_CTRL(i), value);
}

/* power down host ip */
mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN);

- if (!suspend)
- return 0;
+ return 0;
+}
+
+int ssusb_host_resume(struct ssusb_mtk *ssusb, bool p0_skipped)
+{
+ void __iomem *ibase = ssusb->ippc_base;
+ int u3p_skip_msk = ssusb->u3p_dis_msk;
+ int u2p_skip_msk = ssusb->u2p_dis_msk;
+ int num_u3p = ssusb->u3_ports;
+ int num_u2p = ssusb->u2_ports;
+ u32 value;
+ int i;
+
+ if (p0_skipped) {
+ u2p_skip_msk |= 0x1;
+ if (ssusb->otg_switch.is_u3_drd)
+ u3p_skip_msk |= 0x1;
+ }
+
+ /* power on host ip */
+ mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN);
+
+ /* power on u3 ports except skipped ones */
+ for (i = 0; i < num_u3p; i++) {
+ if ((0x1 << i) & u3p_skip_msk)
+ continue;
+
+ value = mtu3_readl(ibase, SSUSB_U3_CTRL(i));
+ value &= ~SSUSB_U3_PORT_PDN;
+ mtu3_writel(ibase, SSUSB_U3_CTRL(i), value);
+ }
+
+ /* power on all u2 ports except skipped ones */
+ for (i = 0; i < num_u2p; i++) {
+ if ((0x1 << i) & u2p_skip_msk)
+ continue;
+
+ value = mtu3_readl(ibase, SSUSB_U2_CTRL(i));
+ value &= ~SSUSB_U2_PORT_PDN;
+ mtu3_writel(ibase, SSUSB_U2_CTRL(i), value);
+ }
+
+ return 0;
+}
+
+/* here not skip port0 due to PDN can be set repeatedly */
+int ssusb_host_suspend(struct ssusb_mtk *ssusb)
+{
+ void __iomem *ibase = ssusb->ippc_base;
+ int num_u3p = ssusb->u3_ports;
+ int num_u2p = ssusb->u2_ports;
+ u32 value;
+ int ret;
+ int i;
+
+ /* power down u3 ports except skipped ones */
+ for (i = 0; i < num_u3p; i++) {
+ if ((0x1 << i) & ssusb->u3p_dis_msk)
+ continue;
+
+ value = mtu3_readl(ibase, SSUSB_U3_CTRL(i));
+ value |= SSUSB_U3_PORT_PDN;
+ mtu3_writel(ibase, SSUSB_U3_CTRL(i), value);
+ }
+
+ /* power down u2 ports except skipped ones */
+ for (i = 0; i < num_u2p; i++) {
+ if ((0x1 << i) & ssusb->u2p_dis_msk)
+ continue;
+
+ value = mtu3_readl(ibase, SSUSB_U2_CTRL(i));
+ value |= SSUSB_U2_PORT_PDN;
+ mtu3_writel(ibase, SSUSB_U2_CTRL(i), value);
+ }
+
+ /* power down host ip */
+ mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN);

/* wait for host ip to sleep */
ret = readl_poll_timeout(ibase + U3D_SSUSB_IP_PW_STS1, value,
@@ -237,7 +309,7 @@ static void ssusb_host_cleanup(struct ssusb_mtk *ssusb)
if (ssusb->is_host)
ssusb_set_vbus(&ssusb->otg_switch, 0);

- ssusb_host_disable(ssusb, false);
+ ssusb_host_disable(ssusb);
}

/*
diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c
index 5162b9988dde..a906b24723e6 100644
--- a/drivers/usb/mtu3/mtu3_plat.c
+++ b/drivers/usb/mtu3/mtu3_plat.c
@@ -411,7 +411,7 @@ static int __maybe_unused mtu3_suspend(struct device *dev)
if (!ssusb->is_host)
return 0;

- ssusb_host_disable(ssusb, true);
+ ssusb_host_suspend(ssusb);
ssusb_phy_power_off(ssusb);
clk_bulk_disable_unprepare(BULK_CLKS_CNT, ssusb->clks);
ssusb_wakeup_set(ssusb, true);
@@ -438,7 +438,7 @@ static int __maybe_unused mtu3_resume(struct device *dev)
if (ret)
goto phy_err;

- ssusb_host_enable(ssusb);
+ ssusb_host_resume(ssusb, false);

return 0;

--
2.18.0

2021-07-15 09:11:32

by Chunfeng Yun

[permalink] [raw]
Subject: [PATCH v3 12/13] 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: Chunfeng Yun <[email protected]>
---
v2~3: no changes
---
drivers/usb/mtu3/mtu3.h | 1 +
drivers/usb/mtu3/mtu3_core.c | 68 +++++++++++++++++++++++++++++++++-
drivers/usb/mtu3/mtu3_dr.h | 14 +++++++
drivers/usb/mtu3/mtu3_gadget.c | 5 +++
drivers/usb/mtu3/mtu3_plat.c | 47 +++++++++++++++++------
5 files changed, 121 insertions(+), 14 deletions(-)

diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h
index 171e5b383063..022bbdc54e68 100644
--- a/drivers/usb/mtu3/mtu3.h
+++ b/drivers/usb/mtu3/mtu3.h
@@ -356,6 +356,7 @@ struct mtu3 {
unsigned is_u3_ip:1;
unsigned delayed_status:1;
unsigned gen2cp:1;
+ unsigned connected:1;

u8 address;
u8 test_mode_nr;
diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c
index 648e970d77ba..a800920d38b9 100644
--- a/drivers/usb/mtu3/mtu3_core.c
+++ b/drivers/usb/mtu3/mtu3_core.c
@@ -9,6 +9,7 @@
*/

#include <linux/dma-mapping.h>
+#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_address.h>
@@ -380,6 +381,24 @@ void mtu3_stop(struct mtu3 *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 */
int mtu3_config_ep(struct mtu3 *mtu, struct mtu3_ep *mep,
int interval, int burst, int mult)
@@ -700,11 +719,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;
}
@@ -984,3 +1007,44 @@ void ssusb_gadget_exit(struct ssusb_mtk *ssusb)
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;
+}
diff --git a/drivers/usb/mtu3/mtu3_dr.h b/drivers/usb/mtu3/mtu3_dr.h
index 70dbf4706138..5286f9f5ee18 100644
--- a/drivers/usb/mtu3/mtu3_dr.h
+++ b/drivers/usb/mtu3/mtu3_dr.h
@@ -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)
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)
{
@@ -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 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


diff --git a/drivers/usb/mtu3/mtu3_gadget.c b/drivers/usb/mtu3/mtu3_gadget.c
index 5e21ba05ebf0..7b54631ca335 100644
--- a/drivers/usb/mtu3/mtu3_gadget.c
+++ b/drivers/usb/mtu3/mtu3_gadget.c
@@ -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__,
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);

@@ -482,6 +484,7 @@ static int mtu3_gadget_pullup(struct usb_gadget *gadget, int is_on)
}

spin_unlock_irqrestore(&mtu->lock, flags);
+ pm_runtime_put(mtu->dev);

return 0;
}
@@ -499,6 +502,7 @@ static int mtu3_gadget_start(struct usb_gadget *gadget,
}

dev_dbg(mtu->dev, "bind driver %s\n", driver->function);
+ pm_runtime_get_sync(mtu->dev);

spin_lock_irqsave(&mtu->lock, flags);

@@ -509,6 +513,7 @@ static int mtu3_gadget_start(struct usb_gadget *gadget,
mtu3_start(mtu);

spin_unlock_irqrestore(&mtu->lock, flags);
+ pm_runtime_put(mtu->dev);

return 0;
}
diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c
index 2be890f84c94..e174ada689f2 100644
--- a/drivers/usb/mtu3/mtu3_plat.c
+++ b/drivers/usb/mtu3/mtu3_plat.c
@@ -421,21 +421,32 @@ static int mtu3_remove(struct platform_device *pdev)
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);
@@ -450,9 +461,6 @@ static int mtu3_resume_common(struct device *dev, pm_message_t msg)

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)
@@ -462,7 +470,22 @@ static int mtu3_resume_common(struct device *dev, pm_message_t msg)
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;

--
2.18.0

2021-07-15 09:12:08

by Chunfeng Yun

[permalink] [raw]
Subject: [PATCH v3 08/13] usb: mtu3: support option to disable usb2 ports

Add support to disable specific usb2 host ports, it's useful when
a usb2 port is disabled on some platforms, but enabled on others
for the same SoC.

Signed-off-by: Chunfeng Yun <[email protected]>
---
v2~3: no changes
---
drivers/usb/mtu3/mtu3.h | 4 ++++
drivers/usb/mtu3/mtu3_host.c | 8 +++++++-
drivers/usb/mtu3/mtu3_plat.c | 11 +++++++++--
3 files changed, 20 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h
index bc82c7f97ad6..0ae9b33b50ea 100644
--- a/drivers/usb/mtu3/mtu3.h
+++ b/drivers/usb/mtu3/mtu3.h
@@ -228,6 +228,9 @@ struct otg_switch_mtk {
* host only, device only or dual-role mode
* @u2_ports: number of usb2.0 host ports
* @u3_ports: number of usb3.0 host ports
+ * @u2p_dis_msk: mask of disabling usb2 ports, e.g. bit0==1 to
+ * disable u2port0, bit1==1 to disable u2port1,... etc,
+ * but when use dual-role mode, can't disable u2port0
* @u3p_dis_msk: mask of disabling usb3 ports, for example, bit0==1 to
* disable u3port0, bit1==1 to disable u3port1,... etc
* @dbgfs_root: only used when supports manual dual-role switch via debugfs
@@ -252,6 +255,7 @@ struct ssusb_mtk {
bool is_host;
int u2_ports;
int u3_ports;
+ int u2p_dis_msk;
int u3p_dis_msk;
struct dentry *dbgfs_root;
/* usb wakeup for host mode */
diff --git a/drivers/usb/mtu3/mtu3_host.c b/drivers/usb/mtu3/mtu3_host.c
index 93a1a4c11e1e..6185bac5fecf 100644
--- a/drivers/usb/mtu3/mtu3_host.c
+++ b/drivers/usb/mtu3/mtu3_host.c
@@ -155,6 +155,9 @@ int ssusb_host_enable(struct ssusb_mtk *ssusb)

/* power on and enable all u2 ports */
for (i = 0; i < num_u2p; i++) {
+ if ((0x1 << i) & ssusb->u2p_dis_msk)
+ continue;
+
value = mtu3_readl(ibase, SSUSB_U2_CTRL(i));
value &= ~(SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS);
value |= SSUSB_U2_PORT_HOST_SEL;
@@ -188,8 +191,11 @@ int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend)
mtu3_writel(ibase, SSUSB_U3_CTRL(i), value);
}

- /* power down and disable all u2 ports */
+ /* power down and disable u2 ports except skipped ones */
for (i = 0; i < num_u2p; i++) {
+ if ((0x1 << i) & ssusb->u2p_dis_msk)
+ continue;
+
value = mtu3_readl(ibase, SSUSB_U2_CTRL(i));
value |= SSUSB_U2_PORT_PDN;
value |= suspend ? 0 : SSUSB_U2_PORT_DIS;
diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c
index c0615f6e5cce..5162b9988dde 100644
--- a/drivers/usb/mtu3/mtu3_plat.c
+++ b/drivers/usb/mtu3/mtu3_plat.c
@@ -225,6 +225,8 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb)
/* optional property, ignore the error if it does not exist */
of_property_read_u32(node, "mediatek,u3p-dis-msk",
&ssusb->u3p_dis_msk);
+ of_property_read_u32(node, "mediatek,u2p-dis-msk",
+ &ssusb->u2p_dis_msk);

otg_sx->vbus = devm_regulator_get(dev, "vbus");
if (IS_ERR(otg_sx->vbus)) {
@@ -241,6 +243,9 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb)
of_property_read_bool(node, "enable-manual-drd");
otg_sx->role_sw_used = of_property_read_bool(node, "usb-role-switch");

+ /* can't disable port0 when use dual-role mode */
+ ssusb->u2p_dis_msk &= ~0x1;
+
if (otg_sx->role_sw_used || otg_sx->manual_drd_enabled)
goto out;

@@ -253,9 +258,11 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb)
}

out:
- dev_info(dev, "dr_mode: %d, is_u3_dr: %d, u3p_dis_msk: %x, drd: %s\n",
- ssusb->dr_mode, otg_sx->is_u3_drd, ssusb->u3p_dis_msk,
+ dev_info(dev, "dr_mode: %d, is_u3_dr: %d, drd: %s\n",
+ ssusb->dr_mode, otg_sx->is_u3_drd,
otg_sx->manual_drd_enabled ? "manual" : "auto");
+ dev_info(dev, "u2p_dis_msk: %x, u3p_dis_msk: %x\n",
+ ssusb->u2p_dis_msk, ssusb->u3p_dis_msk);

return 0;
}
--
2.18.0

2021-07-15 09:12:13

by Chunfeng Yun

[permalink] [raw]
Subject: [PATCH v3 11/13] usb: mtu3: add helper to power on/down device

Add helper to power on/down device ports and ip, it will be
used when support device suspend/resume

Signed-off-by: Chunfeng Yun <[email protected]>
---
v2~3: no changes
---
drivers/usb/mtu3/mtu3_core.c | 36 ++++++++++++++++++++++++------------
1 file changed, 24 insertions(+), 12 deletions(-)

diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c
index 6d23acb4fffc..648e970d77ba 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)
{
@@ -333,12 +355,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 +377,7 @@ void mtu3_stop(struct mtu3 *mtu)
mtu3_dev_on_off(mtu, 0);

mtu->is_active = 0;
-
- if (mtu->is_u3_ip)
- mtu3_setbits(mtu->ippc_base, SSUSB_U3_CTRL(0), SSUSB_U3_PORT_PDN);
-
- 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_dev_power_down(mtu);
}

/* for non-ep0 */
--
2.18.0

2021-07-15 10:49:32

by Chunfeng Yun

[permalink] [raw]
Subject: [PATCH v3 03/13] dt-bindings: usb: mtu3: add support property role-switch-default-mode

Add support optional property of role-switch-default-mode,
used to set default mode while usb role is USB_ROLE_NONE when use
usb-role-switch to switch mode.

Acked-by: Rob Herring <[email protected]>
Signed-off-by: Chunfeng Yun <[email protected]>
---
v3: add Acked-by Rob
v2: no changes
---
Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml b/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml
index 3e6f2750f48d..104f1f7edaf0 100644
--- a/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml
+++ b/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml
@@ -116,6 +116,10 @@ properties:
description: Support role switch.
type: boolean

+ role-switch-default-mode:
+ enum: [host, peripheral]
+ default: host
+
connector:
$ref: /connector/usb-connector.yaml#
description:
@@ -184,6 +188,7 @@ patternProperties:
dependencies:
connector: [ 'usb-role-switch' ]
port: [ 'usb-role-switch' ]
+ role-switch-default-mode: [ 'usb-role-switch' ]
wakeup-source: [ 'mediatek,syscon-wakeup' ]

required:
@@ -288,6 +293,7 @@ examples:
wakeup-source;
dr_mode = "otg";
usb-role-switch;
+ role-switch-default-mode = "host";
#address-cells = <1>;
#size-cells = <1>;
ranges;
--
2.18.0

2021-07-15 10:49:58

by Chunfeng Yun

[permalink] [raw]
Subject: [PATCH v3 04/13] dt-bindings: usb: mtu3: add wakeup interrupt

Add an dedicated interrupt which is usually EINT to support runtime PM,
meanwhile add "interrupt-names" property, for backward compatibility,
it's optional and used when wakeup interrupt exists

Acked-by: Rob Herring <[email protected]>
Signed-off-by: Chunfeng Yun <[email protected]>
---
v3: add Acked-by Rob
v2: no changes
---
.../devicetree/bindings/usb/mediatek,mtu3.yaml | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml b/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml
index 104f1f7edaf0..77db1233516e 100644
--- a/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml
+++ b/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml
@@ -38,7 +38,18 @@ properties:
- const: ippc

interrupts:
- maxItems: 1
+ description:
+ use "interrupts-extended" when the interrupts are connected to the
+ separate interrupt controllers
+ minItems: 1
+ items:
+ - description: SSUSB device controller interrupt
+ - description: optional, wakeup interrupt used to support runtime PM
+
+ interrupt-names:
+ items:
+ - const: device
+ - const: wakeup

power-domains:
description: A phandle to USB power domain node to control USB's MTCMOS
--
2.18.0

2021-07-15 10:50:36

by Chunfeng Yun

[permalink] [raw]
Subject: [PATCH v3 06/13] usb: dwc3: drd: use helper to get role-switch-default-mode

Use the new helper usb_get_role_switch_default_mode() to
get property of "role-switch-default-mode"

Signed-off-by: Chunfeng Yun <[email protected]>
---
v2~3: no changes
---
drivers/usb/dwc3/drd.c | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c
index 8fcbac10510c..d7f76835137f 100644
--- a/drivers/usb/dwc3/drd.c
+++ b/drivers/usb/dwc3/drd.c
@@ -541,14 +541,10 @@ static enum usb_role dwc3_usb_role_switch_get(struct usb_role_switch *sw)
static int dwc3_setup_role_switch(struct dwc3 *dwc)
{
struct usb_role_switch_desc dwc3_role_switch = {NULL};
- const char *str;
u32 mode;
- int ret;

- ret = device_property_read_string(dwc->dev, "role-switch-default-mode",
- &str);
- if (ret >= 0 && !strncmp(str, "host", strlen("host"))) {
- dwc->role_switch_default_mode = USB_DR_MODE_HOST;
+ dwc->role_switch_default_mode = usb_get_role_switch_default_mode(dwc->dev);
+ if (dwc->role_switch_default_mode == USB_DR_MODE_HOST) {
mode = DWC3_GCTL_PRTCAP_HOST;
} else {
dwc->role_switch_default_mode = USB_DR_MODE_PERIPHERAL;
--
2.18.0

2021-07-15 11:14:05

by Chunfeng Yun

[permalink] [raw]
Subject: [PATCH v3 05/13] usb: common: add helper to get role-switch-default-mode

Add helper to get "role-switch-default-mode", and convert it
to the corresponding enum usb_dr_mode

Signed-off-by: Chunfeng Yun <[email protected]>
---
v2~3: no changes
---
drivers/usb/common/common.c | 20 ++++++++++++++++++++
include/linux/usb/otg.h | 1 +
2 files changed, 21 insertions(+)

diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c
index 347fb3d3894a..c9bdeb4ddcb5 100644
--- a/drivers/usb/common/common.c
+++ b/drivers/usb/common/common.c
@@ -200,6 +200,26 @@ enum usb_dr_mode usb_get_dr_mode(struct device *dev)
}
EXPORT_SYMBOL_GPL(usb_get_dr_mode);

+/**
+ * usb_get_role_switch_default_mode - Get default mode for given device
+ * @dev: Pointer to the given device
+ *
+ * The function gets string from property 'role-switch-default-mode',
+ * and returns the corresponding enum usb_dr_mode.
+ */
+enum usb_dr_mode usb_get_role_switch_default_mode(struct device *dev)
+{
+ const char *str;
+ int ret;
+
+ ret = device_property_read_string(dev, "role-switch-default-mode", &str);
+ if (ret < 0)
+ return USB_DR_MODE_UNKNOWN;
+
+ return usb_get_dr_mode_from_string(str);
+}
+EXPORT_SYMBOL_GPL(usb_get_role_switch_default_mode);
+
/**
* usb_decode_interval - Decode bInterval into the time expressed in 1us unit
* @epd: The descriptor of the endpoint
diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
index 7ceeecbb9e02..6475f880be37 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -128,5 +128,6 @@ enum usb_dr_mode {
* and returns the corresponding enum usb_dr_mode
*/
extern enum usb_dr_mode usb_get_dr_mode(struct device *dev);
+extern enum usb_dr_mode usb_get_role_switch_default_mode(struct device *dev);

#endif /* __LINUX_USB_OTG_H */
--
2.18.0

2021-07-15 11:20:25

by Chunfeng Yun

[permalink] [raw]
Subject: [PATCH v3 13/13] usb: mtu3: support suspend/resume for dual-role mode

Support suspend/resume for dual-role mode including the single
port and multi-ports supported by host controller, when the host
supports mult-ports, only port0 (u2/u3) is used to support dual
role mode.

Signed-off-by: Chunfeng Yun <[email protected]>
---
v2~3: no changes
---
drivers/usb/mtu3/mtu3_core.c | 32 +++++++-------
drivers/usb/mtu3/mtu3_dr.c | 2 +
drivers/usb/mtu3/mtu3_dr.h | 8 ++++
drivers/usb/mtu3/mtu3_host.c | 10 +----
drivers/usb/mtu3/mtu3_plat.c | 84 ++++++++++++++++++++++++++----------
5 files changed, 89 insertions(+), 47 deletions(-)

diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c
index a800920d38b9..f90e5cdec614 100644
--- a/drivers/usb/mtu3/mtu3_core.c
+++ b/drivers/usb/mtu3/mtu3_core.c
@@ -9,7 +9,6 @@
*/

#include <linux/dma-mapping.h>
-#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_address.h>
@@ -1008,12 +1007,25 @@ void ssusb_gadget_exit(struct ssusb_mtk *ssusb)
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;
- void __iomem *ibase = mtu->ippc_base;
- u32 value;
- int ret = 0;

if (!mtu->gadget_driver)
return 0;
@@ -1024,17 +1036,7 @@ int ssusb_gadget_suspend(struct ssusb_mtk *ssusb, pm_message_t msg)
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;
+ return 0;
}

int ssusb_gadget_resume(struct ssusb_mtk *ssusb, pm_message_t msg)
diff --git a/drivers/usb/mtu3/mtu3_dr.c b/drivers/usb/mtu3/mtu3_dr.c
index 30e7e5fc0f88..a6b04831b20b 100644
--- a/drivers/usb/mtu3/mtu3_dr.c
+++ b/drivers/usb/mtu3/mtu3_dr.c
@@ -149,6 +149,7 @@ static void ssusb_mode_sw_work(struct work_struct *work)

dev_dbg(ssusb->dev, "set role : %s\n", usb_role_string(desired_role));
mtu3_dbg_trace(ssusb->dev, "set role : %s", usb_role_string(desired_role));
+ pm_runtime_get_sync(ssusb->dev);

switch (desired_role) {
case USB_ROLE_HOST:
@@ -169,6 +170,7 @@ static void ssusb_mode_sw_work(struct work_struct *work)
default:
dev_err(ssusb->dev, "invalid role\n");
}
+ pm_runtime_put(ssusb->dev);
}

static void ssusb_set_mode(struct otg_switch_mtk *otg_sx, enum usb_role role)
diff --git a/drivers/usb/mtu3/mtu3_dr.h b/drivers/usb/mtu3/mtu3_dr.h
index 5286f9f5ee18..e325508bddf4 100644
--- a/drivers/usb/mtu3/mtu3_dr.h
+++ b/drivers/usb/mtu3/mtu3_dr.h
@@ -59,6 +59,8 @@ 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);
+bool ssusb_gadget_ip_sleep_check(struct ssusb_mtk *ssusb);
+
#else
static inline int ssusb_gadget_init(struct ssusb_mtk *ssusb)
{
@@ -79,6 +81,12 @@ ssusb_gadget_resume(struct ssusb_mtk *ssusb, pm_message_t msg)
{
return 0;
}
+
+static inline bool ssusb_gadget_ip_sleep_check(struct ssusb_mtk *ssusb)
+{
+ return true;
+}
+
#endif


diff --git a/drivers/usb/mtu3/mtu3_host.c b/drivers/usb/mtu3/mtu3_host.c
index a0a6a181b752..7d528f3c2482 100644
--- a/drivers/usb/mtu3/mtu3_host.c
+++ b/drivers/usb/mtu3/mtu3_host.c
@@ -8,7 +8,6 @@
*/

#include <linux/clk.h>
-#include <linux/iopoll.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
@@ -254,7 +253,6 @@ int ssusb_host_suspend(struct ssusb_mtk *ssusb)
int num_u3p = ssusb->u3_ports;
int num_u2p = ssusb->u2_ports;
u32 value;
- int ret;
int i;

/* power down u3 ports except skipped ones */
@@ -280,13 +278,7 @@ int ssusb_host_suspend(struct ssusb_mtk *ssusb)
/* power down host ip */
mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN);

- /* wait for host ip to sleep */
- ret = readl_poll_timeout(ibase + U3D_SSUSB_IP_PW_STS1, value,
- (value & SSUSB_IP_SLEEP_STS), 100, 100000);
- if (ret)
- dev_err(ssusb->dev, "ip sleep failed!!!\n");
-
- return ret;
+ return 0;
}

static void ssusb_host_setup(struct ssusb_mtk *ssusb)
diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c
index e174ada689f2..81266f34986f 100644
--- a/drivers/usb/mtu3/mtu3_plat.c
+++ b/drivers/usb/mtu3/mtu3_plat.c
@@ -45,6 +45,29 @@ int ssusb_check_clocks(struct ssusb_mtk *ssusb, u32 ex_clks)
return 0;
}

+static int wait_for_ip_sleep(struct ssusb_mtk *ssusb)
+{
+ bool sleep_check = true;
+ u32 value;
+ int ret;
+
+ if (!ssusb->is_host)
+ sleep_check = ssusb_gadget_ip_sleep_check(ssusb);
+
+ if (!sleep_check)
+ return 0;
+
+ /* wait for ip enter sleep mode */
+ ret = readl_poll_timeout(ssusb->ippc_base + U3D_SSUSB_IP_PW_STS1, value,
+ (value & SSUSB_IP_SLEEP_STS), 100, 100000);
+ if (ret) {
+ dev_err(ssusb->dev, "ip sleep failed!!!\n");
+ ret = -EBUSY;
+ }
+
+ return ret;
+}
+
static int ssusb_phy_init(struct ssusb_mtk *ssusb)
{
int i;
@@ -421,6 +444,28 @@ static int mtu3_remove(struct platform_device *pdev)
return 0;
}

+static int resume_ip_and_ports(struct ssusb_mtk *ssusb, pm_message_t msg)
+{
+ 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:
+ ssusb_host_resume(ssusb, !ssusb->is_host);
+ if (!ssusb->is_host)
+ ssusb_gadget_resume(ssusb, msg);
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int mtu3_suspend_common(struct device *dev, pm_message_t msg)
{
struct ssusb_mtk *ssusb = dev_get_drvdata(dev);
@@ -432,26 +477,36 @@ static int mtu3_suspend_common(struct device *dev, pm_message_t msg)
case USB_DR_MODE_PERIPHERAL:
ret = ssusb_gadget_suspend(ssusb, msg);
if (ret)
- return ret;
+ goto err;

break;
case USB_DR_MODE_HOST:
ssusb_host_suspend(ssusb);
break;
case USB_DR_MODE_OTG:
- if (!ssusb->is_host)
- return 0;
-
+ if (!ssusb->is_host) {
+ ret = ssusb_gadget_suspend(ssusb, msg);
+ if (ret)
+ goto err;
+ }
ssusb_host_suspend(ssusb);
break;
default:
return -EINVAL;
}
+
+ ret = wait_for_ip_sleep(ssusb);
+ if (ret)
+ goto sleep_err;
+
ssusb_phy_power_off(ssusb);
clk_bulk_disable_unprepare(BULK_CLKS_CNT, ssusb->clks);
ssusb_wakeup_set(ssusb, true);

- return 0;
+sleep_err:
+ resume_ip_and_ports(ssusb, msg);
+err:
+ return ret;
}

static int mtu3_resume_common(struct device *dev, pm_message_t msg)
@@ -470,24 +525,7 @@ static int mtu3_resume_common(struct device *dev, pm_message_t msg)
if (ret)
goto phy_err;

- 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 resume_ip_and_ports(ssusb, msg);

phy_err:
clk_bulk_disable_unprepare(BULK_CLKS_CNT, ssusb->clks);
--
2.18.0

2021-07-22 02:53:28

by Rob Herring

[permalink] [raw]
Subject: Re: [PATCH v3 02/13] dt-bindings: usb: mtu3: add optional property to disable usb2 ports

On Thu, 15 Jul 2021 17:07:47 +0800, Chunfeng Yun wrote:
> Add support to disable specific usb2 host ports, it's useful when
> a usb2 port is disabled on some platforms, but enabled on others for
> the same SoC, another case is that the different package may support
> different number of ports.
>
> Signed-off-by: Chunfeng Yun <[email protected]>
> ---
> Due to there is property 'mediatek,u3p-dis-msk', prefer to use
> a similar name for the similar opertions, not spell out 'mask'
> as suggested by Rob.
>
> v2~3: no changes
> ---
> Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml | 6 ++++++
> 1 file changed, 6 insertions(+)
>

Acked-by: Rob Herring <[email protected]>