162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2016 MediaTek Inc.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/dma-mapping.h>
962306a36Sopenharmony_ci#include <linux/iopoll.h>
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/of_address.h>
1362306a36Sopenharmony_ci#include <linux/of_irq.h>
1462306a36Sopenharmony_ci#include <linux/platform_device.h>
1562306a36Sopenharmony_ci#include <linux/pm_wakeirq.h>
1662306a36Sopenharmony_ci#include <linux/reset.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "mtu3.h"
1962306a36Sopenharmony_ci#include "mtu3_dr.h"
2062306a36Sopenharmony_ci#include "mtu3_debug.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/* u2-port0 should be powered on and enabled; */
2362306a36Sopenharmony_ciint ssusb_check_clocks(struct ssusb_mtk *ssusb, u32 ex_clks)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	void __iomem *ibase = ssusb->ippc_base;
2662306a36Sopenharmony_ci	u32 value, check_val;
2762306a36Sopenharmony_ci	int ret;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	check_val = ex_clks | SSUSB_SYS125_RST_B_STS | SSUSB_SYSPLL_STABLE |
3062306a36Sopenharmony_ci			SSUSB_REF_RST_B_STS;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	ret = readl_poll_timeout(ibase + U3D_SSUSB_IP_PW_STS1, value,
3362306a36Sopenharmony_ci			(check_val == (value & check_val)), 100, 20000);
3462306a36Sopenharmony_ci	if (ret) {
3562306a36Sopenharmony_ci		dev_err(ssusb->dev, "clks of sts1 are not stable!\n");
3662306a36Sopenharmony_ci		return ret;
3762306a36Sopenharmony_ci	}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	ret = readl_poll_timeout(ibase + U3D_SSUSB_IP_PW_STS2, value,
4062306a36Sopenharmony_ci			(value & SSUSB_U2_MAC_SYS_RST_B_STS), 100, 10000);
4162306a36Sopenharmony_ci	if (ret) {
4262306a36Sopenharmony_ci		dev_err(ssusb->dev, "mac2 clock is not stable\n");
4362306a36Sopenharmony_ci		return ret;
4462306a36Sopenharmony_ci	}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	return 0;
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic int wait_for_ip_sleep(struct ssusb_mtk *ssusb)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	bool sleep_check = true;
5262306a36Sopenharmony_ci	u32 value;
5362306a36Sopenharmony_ci	int ret;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	if (!ssusb->is_host)
5662306a36Sopenharmony_ci		sleep_check = ssusb_gadget_ip_sleep_check(ssusb);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	if (!sleep_check)
5962306a36Sopenharmony_ci		return 0;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	/* wait for ip enter sleep mode */
6262306a36Sopenharmony_ci	ret = readl_poll_timeout(ssusb->ippc_base + U3D_SSUSB_IP_PW_STS1, value,
6362306a36Sopenharmony_ci				 (value & SSUSB_IP_SLEEP_STS), 100, 100000);
6462306a36Sopenharmony_ci	if (ret) {
6562306a36Sopenharmony_ci		dev_err(ssusb->dev, "ip sleep failed!!!\n");
6662306a36Sopenharmony_ci		ret = -EBUSY;
6762306a36Sopenharmony_ci	} else {
6862306a36Sopenharmony_ci		/* workaround: avoid wrong wakeup signal latch for some soc */
6962306a36Sopenharmony_ci		usleep_range(100, 200);
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return ret;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic int ssusb_phy_init(struct ssusb_mtk *ssusb)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	int i;
7862306a36Sopenharmony_ci	int ret;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	for (i = 0; i < ssusb->num_phys; i++) {
8162306a36Sopenharmony_ci		ret = phy_init(ssusb->phys[i]);
8262306a36Sopenharmony_ci		if (ret)
8362306a36Sopenharmony_ci			goto exit_phy;
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci	return 0;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ciexit_phy:
8862306a36Sopenharmony_ci	for (; i > 0; i--)
8962306a36Sopenharmony_ci		phy_exit(ssusb->phys[i - 1]);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	return ret;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic int ssusb_phy_exit(struct ssusb_mtk *ssusb)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	int i;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	for (i = 0; i < ssusb->num_phys; i++)
9962306a36Sopenharmony_ci		phy_exit(ssusb->phys[i]);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	return 0;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic int ssusb_phy_power_on(struct ssusb_mtk *ssusb)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	int i;
10762306a36Sopenharmony_ci	int ret;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	for (i = 0; i < ssusb->num_phys; i++) {
11062306a36Sopenharmony_ci		ret = phy_power_on(ssusb->phys[i]);
11162306a36Sopenharmony_ci		if (ret)
11262306a36Sopenharmony_ci			goto power_off_phy;
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci	return 0;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cipower_off_phy:
11762306a36Sopenharmony_ci	for (; i > 0; i--)
11862306a36Sopenharmony_ci		phy_power_off(ssusb->phys[i - 1]);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	return ret;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic void ssusb_phy_power_off(struct ssusb_mtk *ssusb)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	unsigned int i;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	for (i = 0; i < ssusb->num_phys; i++)
12862306a36Sopenharmony_ci		phy_power_off(ssusb->phys[i]);
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic int ssusb_rscs_init(struct ssusb_mtk *ssusb)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	int ret = 0;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	ret = regulator_enable(ssusb->vusb33);
13662306a36Sopenharmony_ci	if (ret) {
13762306a36Sopenharmony_ci		dev_err(ssusb->dev, "failed to enable vusb33\n");
13862306a36Sopenharmony_ci		goto vusb33_err;
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	ret = clk_bulk_prepare_enable(BULK_CLKS_CNT, ssusb->clks);
14262306a36Sopenharmony_ci	if (ret)
14362306a36Sopenharmony_ci		goto clks_err;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	ret = ssusb_phy_init(ssusb);
14662306a36Sopenharmony_ci	if (ret) {
14762306a36Sopenharmony_ci		dev_err(ssusb->dev, "failed to init phy\n");
14862306a36Sopenharmony_ci		goto phy_init_err;
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	ret = ssusb_phy_power_on(ssusb);
15262306a36Sopenharmony_ci	if (ret) {
15362306a36Sopenharmony_ci		dev_err(ssusb->dev, "failed to power on phy\n");
15462306a36Sopenharmony_ci		goto phy_err;
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	return 0;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ciphy_err:
16062306a36Sopenharmony_ci	ssusb_phy_exit(ssusb);
16162306a36Sopenharmony_ciphy_init_err:
16262306a36Sopenharmony_ci	clk_bulk_disable_unprepare(BULK_CLKS_CNT, ssusb->clks);
16362306a36Sopenharmony_ciclks_err:
16462306a36Sopenharmony_ci	regulator_disable(ssusb->vusb33);
16562306a36Sopenharmony_civusb33_err:
16662306a36Sopenharmony_ci	return ret;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic void ssusb_rscs_exit(struct ssusb_mtk *ssusb)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	clk_bulk_disable_unprepare(BULK_CLKS_CNT, ssusb->clks);
17262306a36Sopenharmony_ci	regulator_disable(ssusb->vusb33);
17362306a36Sopenharmony_ci	ssusb_phy_power_off(ssusb);
17462306a36Sopenharmony_ci	ssusb_phy_exit(ssusb);
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic void ssusb_ip_sw_reset(struct ssusb_mtk *ssusb)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	/* reset whole ip (xhci & u3d) */
18062306a36Sopenharmony_ci	mtu3_setbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST);
18162306a36Sopenharmony_ci	udelay(1);
18262306a36Sopenharmony_ci	mtu3_clrbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	/*
18562306a36Sopenharmony_ci	 * device ip may be powered on in firmware/BROM stage before entering
18662306a36Sopenharmony_ci	 * kernel stage;
18762306a36Sopenharmony_ci	 * power down device ip, otherwise ip-sleep will fail when working as
18862306a36Sopenharmony_ci	 * host only mode
18962306a36Sopenharmony_ci	 */
19062306a36Sopenharmony_ci	mtu3_setbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic void ssusb_u3_drd_check(struct ssusb_mtk *ssusb)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
19662306a36Sopenharmony_ci	u32 dev_u3p_num;
19762306a36Sopenharmony_ci	u32 host_u3p_num;
19862306a36Sopenharmony_ci	u32 value;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	/* u3 port0 is disabled */
20162306a36Sopenharmony_ci	if (ssusb->u3p_dis_msk & BIT(0)) {
20262306a36Sopenharmony_ci		otg_sx->is_u3_drd = false;
20362306a36Sopenharmony_ci		goto out;
20462306a36Sopenharmony_ci	}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	value = mtu3_readl(ssusb->ippc_base, U3D_SSUSB_IP_DEV_CAP);
20762306a36Sopenharmony_ci	dev_u3p_num = SSUSB_IP_DEV_U3_PORT_NUM(value);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	value = mtu3_readl(ssusb->ippc_base, U3D_SSUSB_IP_XHCI_CAP);
21062306a36Sopenharmony_ci	host_u3p_num = SSUSB_IP_XHCI_U3_PORT_NUM(value);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	otg_sx->is_u3_drd = !!(dev_u3p_num && host_u3p_num);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ciout:
21562306a36Sopenharmony_ci	dev_info(ssusb->dev, "usb3-drd: %d\n", otg_sx->is_u3_drd);
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	struct device_node *node = pdev->dev.of_node;
22162306a36Sopenharmony_ci	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
22262306a36Sopenharmony_ci	struct clk_bulk_data *clks = ssusb->clks;
22362306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
22462306a36Sopenharmony_ci	int i;
22562306a36Sopenharmony_ci	int ret;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	ssusb->vusb33 = devm_regulator_get(dev, "vusb33");
22862306a36Sopenharmony_ci	if (IS_ERR(ssusb->vusb33)) {
22962306a36Sopenharmony_ci		dev_err(dev, "failed to get vusb33\n");
23062306a36Sopenharmony_ci		return PTR_ERR(ssusb->vusb33);
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	clks[0].id = "sys_ck";
23462306a36Sopenharmony_ci	clks[1].id = "ref_ck";
23562306a36Sopenharmony_ci	clks[2].id = "mcu_ck";
23662306a36Sopenharmony_ci	clks[3].id = "dma_ck";
23762306a36Sopenharmony_ci	clks[4].id = "xhci_ck";
23862306a36Sopenharmony_ci	clks[5].id = "frmcnt_ck";
23962306a36Sopenharmony_ci	ret = devm_clk_bulk_get_optional(dev, BULK_CLKS_CNT, clks);
24062306a36Sopenharmony_ci	if (ret)
24162306a36Sopenharmony_ci		return ret;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	ssusb->num_phys = of_count_phandle_with_args(node,
24462306a36Sopenharmony_ci			"phys", "#phy-cells");
24562306a36Sopenharmony_ci	if (ssusb->num_phys > 0) {
24662306a36Sopenharmony_ci		ssusb->phys = devm_kcalloc(dev, ssusb->num_phys,
24762306a36Sopenharmony_ci					sizeof(*ssusb->phys), GFP_KERNEL);
24862306a36Sopenharmony_ci		if (!ssusb->phys)
24962306a36Sopenharmony_ci			return -ENOMEM;
25062306a36Sopenharmony_ci	} else {
25162306a36Sopenharmony_ci		ssusb->num_phys = 0;
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	for (i = 0; i < ssusb->num_phys; i++) {
25562306a36Sopenharmony_ci		ssusb->phys[i] = devm_of_phy_get_by_index(dev, node, i);
25662306a36Sopenharmony_ci		if (IS_ERR(ssusb->phys[i])) {
25762306a36Sopenharmony_ci			dev_err(dev, "failed to get phy-%d\n", i);
25862306a36Sopenharmony_ci			return PTR_ERR(ssusb->phys[i]);
25962306a36Sopenharmony_ci		}
26062306a36Sopenharmony_ci	}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	ssusb->ippc_base = devm_platform_ioremap_resource_byname(pdev, "ippc");
26362306a36Sopenharmony_ci	if (IS_ERR(ssusb->ippc_base))
26462306a36Sopenharmony_ci		return PTR_ERR(ssusb->ippc_base);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	ssusb->wakeup_irq = platform_get_irq_byname_optional(pdev, "wakeup");
26762306a36Sopenharmony_ci	if (ssusb->wakeup_irq == -EPROBE_DEFER)
26862306a36Sopenharmony_ci		return ssusb->wakeup_irq;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	ssusb->dr_mode = usb_get_dr_mode(dev);
27162306a36Sopenharmony_ci	if (ssusb->dr_mode == USB_DR_MODE_UNKNOWN)
27262306a36Sopenharmony_ci		ssusb->dr_mode = USB_DR_MODE_OTG;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	of_property_read_u32(node, "mediatek,u3p-dis-msk", &ssusb->u3p_dis_msk);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	if (ssusb->dr_mode == USB_DR_MODE_PERIPHERAL)
27762306a36Sopenharmony_ci		goto out;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	/* if host role is supported */
28062306a36Sopenharmony_ci	ret = ssusb_wakeup_of_property_parse(ssusb, node);
28162306a36Sopenharmony_ci	if (ret) {
28262306a36Sopenharmony_ci		dev_err(dev, "failed to parse uwk property\n");
28362306a36Sopenharmony_ci		return ret;
28462306a36Sopenharmony_ci	}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	/* optional property, ignore the error if it does not exist */
28762306a36Sopenharmony_ci	of_property_read_u32(node, "mediatek,u2p-dis-msk",
28862306a36Sopenharmony_ci			     &ssusb->u2p_dis_msk);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	otg_sx->vbus = devm_regulator_get(dev, "vbus");
29162306a36Sopenharmony_ci	if (IS_ERR(otg_sx->vbus)) {
29262306a36Sopenharmony_ci		dev_err(dev, "failed to get vbus\n");
29362306a36Sopenharmony_ci		return PTR_ERR(otg_sx->vbus);
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	if (ssusb->dr_mode == USB_DR_MODE_HOST)
29762306a36Sopenharmony_ci		goto out;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	/* if dual-role mode is supported */
30062306a36Sopenharmony_ci	otg_sx->manual_drd_enabled =
30162306a36Sopenharmony_ci		of_property_read_bool(node, "enable-manual-drd");
30262306a36Sopenharmony_ci	otg_sx->role_sw_used = of_property_read_bool(node, "usb-role-switch");
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	/* can't disable port0 when use dual-role mode */
30562306a36Sopenharmony_ci	ssusb->u2p_dis_msk &= ~0x1;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (otg_sx->role_sw_used || otg_sx->manual_drd_enabled)
30862306a36Sopenharmony_ci		goto out;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	if (of_property_read_bool(node, "extcon")) {
31162306a36Sopenharmony_ci		otg_sx->edev = extcon_get_edev_by_phandle(ssusb->dev, 0);
31262306a36Sopenharmony_ci		if (IS_ERR(otg_sx->edev)) {
31362306a36Sopenharmony_ci			return dev_err_probe(dev, PTR_ERR(otg_sx->edev),
31462306a36Sopenharmony_ci					     "couldn't get extcon device\n");
31562306a36Sopenharmony_ci		}
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ciout:
31962306a36Sopenharmony_ci	dev_info(dev, "dr_mode: %d, drd: %s\n", ssusb->dr_mode,
32062306a36Sopenharmony_ci		 otg_sx->manual_drd_enabled ? "manual" : "auto");
32162306a36Sopenharmony_ci	dev_info(dev, "u2p_dis_msk: %x, u3p_dis_msk: %x\n",
32262306a36Sopenharmony_ci		 ssusb->u2p_dis_msk, ssusb->u3p_dis_msk);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	return 0;
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_cistatic int mtu3_probe(struct platform_device *pdev)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	struct device_node *node = pdev->dev.of_node;
33062306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
33162306a36Sopenharmony_ci	struct ssusb_mtk *ssusb;
33262306a36Sopenharmony_ci	int ret = -ENOMEM;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	/* all elements are set to ZERO as default value */
33562306a36Sopenharmony_ci	ssusb = devm_kzalloc(dev, sizeof(*ssusb), GFP_KERNEL);
33662306a36Sopenharmony_ci	if (!ssusb)
33762306a36Sopenharmony_ci		return -ENOMEM;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
34062306a36Sopenharmony_ci	if (ret) {
34162306a36Sopenharmony_ci		dev_err(dev, "No suitable DMA config available\n");
34262306a36Sopenharmony_ci		return -ENOTSUPP;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	platform_set_drvdata(pdev, ssusb);
34662306a36Sopenharmony_ci	ssusb->dev = dev;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	ret = get_ssusb_rscs(pdev, ssusb);
34962306a36Sopenharmony_ci	if (ret)
35062306a36Sopenharmony_ci		return ret;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	ssusb_debugfs_create_root(ssusb);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	/* enable power domain */
35562306a36Sopenharmony_ci	pm_runtime_set_active(dev);
35662306a36Sopenharmony_ci	pm_runtime_use_autosuspend(dev);
35762306a36Sopenharmony_ci	pm_runtime_set_autosuspend_delay(dev, 4000);
35862306a36Sopenharmony_ci	pm_runtime_enable(dev);
35962306a36Sopenharmony_ci	pm_runtime_get_sync(dev);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	device_init_wakeup(dev, true);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	ret = ssusb_rscs_init(ssusb);
36462306a36Sopenharmony_ci	if (ret)
36562306a36Sopenharmony_ci		goto comm_init_err;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	if (ssusb->wakeup_irq > 0) {
36862306a36Sopenharmony_ci		ret = dev_pm_set_dedicated_wake_irq_reverse(dev, ssusb->wakeup_irq);
36962306a36Sopenharmony_ci		if (ret) {
37062306a36Sopenharmony_ci			dev_err(dev, "failed to set wakeup irq %d\n", ssusb->wakeup_irq);
37162306a36Sopenharmony_ci			goto comm_exit;
37262306a36Sopenharmony_ci		}
37362306a36Sopenharmony_ci		dev_info(dev, "wakeup irq %d\n", ssusb->wakeup_irq);
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	ret = device_reset_optional(dev);
37762306a36Sopenharmony_ci	if (ret) {
37862306a36Sopenharmony_ci		dev_err_probe(dev, ret, "failed to reset controller\n");
37962306a36Sopenharmony_ci		goto comm_exit;
38062306a36Sopenharmony_ci	}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	ssusb_ip_sw_reset(ssusb);
38362306a36Sopenharmony_ci	ssusb_u3_drd_check(ssusb);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_USB_MTU3_HOST))
38662306a36Sopenharmony_ci		ssusb->dr_mode = USB_DR_MODE_HOST;
38762306a36Sopenharmony_ci	else if (IS_ENABLED(CONFIG_USB_MTU3_GADGET))
38862306a36Sopenharmony_ci		ssusb->dr_mode = USB_DR_MODE_PERIPHERAL;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	/* default as host */
39162306a36Sopenharmony_ci	ssusb->is_host = !(ssusb->dr_mode == USB_DR_MODE_PERIPHERAL);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	switch (ssusb->dr_mode) {
39462306a36Sopenharmony_ci	case USB_DR_MODE_PERIPHERAL:
39562306a36Sopenharmony_ci		ret = ssusb_gadget_init(ssusb);
39662306a36Sopenharmony_ci		if (ret) {
39762306a36Sopenharmony_ci			dev_err(dev, "failed to initialize gadget\n");
39862306a36Sopenharmony_ci			goto comm_exit;
39962306a36Sopenharmony_ci		}
40062306a36Sopenharmony_ci		break;
40162306a36Sopenharmony_ci	case USB_DR_MODE_HOST:
40262306a36Sopenharmony_ci		ret = ssusb_host_init(ssusb, node);
40362306a36Sopenharmony_ci		if (ret) {
40462306a36Sopenharmony_ci			dev_err(dev, "failed to initialize host\n");
40562306a36Sopenharmony_ci			goto comm_exit;
40662306a36Sopenharmony_ci		}
40762306a36Sopenharmony_ci		break;
40862306a36Sopenharmony_ci	case USB_DR_MODE_OTG:
40962306a36Sopenharmony_ci		ret = ssusb_gadget_init(ssusb);
41062306a36Sopenharmony_ci		if (ret) {
41162306a36Sopenharmony_ci			dev_err(dev, "failed to initialize gadget\n");
41262306a36Sopenharmony_ci			goto comm_exit;
41362306a36Sopenharmony_ci		}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci		ret = ssusb_host_init(ssusb, node);
41662306a36Sopenharmony_ci		if (ret) {
41762306a36Sopenharmony_ci			dev_err(dev, "failed to initialize host\n");
41862306a36Sopenharmony_ci			goto gadget_exit;
41962306a36Sopenharmony_ci		}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci		ret = ssusb_otg_switch_init(ssusb);
42262306a36Sopenharmony_ci		if (ret) {
42362306a36Sopenharmony_ci			dev_err(dev, "failed to initialize switch\n");
42462306a36Sopenharmony_ci			goto host_exit;
42562306a36Sopenharmony_ci		}
42662306a36Sopenharmony_ci		break;
42762306a36Sopenharmony_ci	default:
42862306a36Sopenharmony_ci		dev_err(dev, "unsupported mode: %d\n", ssusb->dr_mode);
42962306a36Sopenharmony_ci		ret = -EINVAL;
43062306a36Sopenharmony_ci		goto comm_exit;
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	device_enable_async_suspend(dev);
43462306a36Sopenharmony_ci	pm_runtime_mark_last_busy(dev);
43562306a36Sopenharmony_ci	pm_runtime_put_autosuspend(dev);
43662306a36Sopenharmony_ci	pm_runtime_forbid(dev);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	return 0;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_cihost_exit:
44162306a36Sopenharmony_ci	ssusb_host_exit(ssusb);
44262306a36Sopenharmony_cigadget_exit:
44362306a36Sopenharmony_ci	ssusb_gadget_exit(ssusb);
44462306a36Sopenharmony_cicomm_exit:
44562306a36Sopenharmony_ci	ssusb_rscs_exit(ssusb);
44662306a36Sopenharmony_cicomm_init_err:
44762306a36Sopenharmony_ci	pm_runtime_put_noidle(dev);
44862306a36Sopenharmony_ci	pm_runtime_disable(dev);
44962306a36Sopenharmony_ci	ssusb_debugfs_remove_root(ssusb);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	return ret;
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_cistatic int mtu3_remove(struct platform_device *pdev)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	struct ssusb_mtk *ssusb = platform_get_drvdata(pdev);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	pm_runtime_get_sync(&pdev->dev);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	switch (ssusb->dr_mode) {
46162306a36Sopenharmony_ci	case USB_DR_MODE_PERIPHERAL:
46262306a36Sopenharmony_ci		ssusb_gadget_exit(ssusb);
46362306a36Sopenharmony_ci		break;
46462306a36Sopenharmony_ci	case USB_DR_MODE_HOST:
46562306a36Sopenharmony_ci		ssusb_host_exit(ssusb);
46662306a36Sopenharmony_ci		break;
46762306a36Sopenharmony_ci	case USB_DR_MODE_OTG:
46862306a36Sopenharmony_ci		ssusb_otg_switch_exit(ssusb);
46962306a36Sopenharmony_ci		ssusb_gadget_exit(ssusb);
47062306a36Sopenharmony_ci		ssusb_host_exit(ssusb);
47162306a36Sopenharmony_ci		break;
47262306a36Sopenharmony_ci	default:
47362306a36Sopenharmony_ci		return -EINVAL;
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	ssusb_rscs_exit(ssusb);
47762306a36Sopenharmony_ci	ssusb_debugfs_remove_root(ssusb);
47862306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
47962306a36Sopenharmony_ci	pm_runtime_put_noidle(&pdev->dev);
48062306a36Sopenharmony_ci	pm_runtime_set_suspended(&pdev->dev);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	return 0;
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cistatic int resume_ip_and_ports(struct ssusb_mtk *ssusb, pm_message_t msg)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	switch (ssusb->dr_mode) {
48862306a36Sopenharmony_ci	case USB_DR_MODE_PERIPHERAL:
48962306a36Sopenharmony_ci		ssusb_gadget_resume(ssusb, msg);
49062306a36Sopenharmony_ci		break;
49162306a36Sopenharmony_ci	case USB_DR_MODE_HOST:
49262306a36Sopenharmony_ci		ssusb_host_resume(ssusb, false);
49362306a36Sopenharmony_ci		break;
49462306a36Sopenharmony_ci	case USB_DR_MODE_OTG:
49562306a36Sopenharmony_ci		ssusb_host_resume(ssusb, !ssusb->is_host);
49662306a36Sopenharmony_ci		if (!ssusb->is_host)
49762306a36Sopenharmony_ci			ssusb_gadget_resume(ssusb, msg);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci		break;
50062306a36Sopenharmony_ci	default:
50162306a36Sopenharmony_ci		return -EINVAL;
50262306a36Sopenharmony_ci	}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	return 0;
50562306a36Sopenharmony_ci}
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_cistatic int mtu3_suspend_common(struct device *dev, pm_message_t msg)
50862306a36Sopenharmony_ci{
50962306a36Sopenharmony_ci	struct ssusb_mtk *ssusb = dev_get_drvdata(dev);
51062306a36Sopenharmony_ci	int ret = 0;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	dev_dbg(dev, "%s\n", __func__);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	switch (ssusb->dr_mode) {
51562306a36Sopenharmony_ci	case USB_DR_MODE_PERIPHERAL:
51662306a36Sopenharmony_ci		ret = ssusb_gadget_suspend(ssusb, msg);
51762306a36Sopenharmony_ci		if (ret)
51862306a36Sopenharmony_ci			goto err;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci		break;
52162306a36Sopenharmony_ci	case USB_DR_MODE_HOST:
52262306a36Sopenharmony_ci		ssusb_host_suspend(ssusb);
52362306a36Sopenharmony_ci		break;
52462306a36Sopenharmony_ci	case USB_DR_MODE_OTG:
52562306a36Sopenharmony_ci		if (!ssusb->is_host) {
52662306a36Sopenharmony_ci			ret = ssusb_gadget_suspend(ssusb, msg);
52762306a36Sopenharmony_ci			if (ret)
52862306a36Sopenharmony_ci				goto err;
52962306a36Sopenharmony_ci		}
53062306a36Sopenharmony_ci		ssusb_host_suspend(ssusb);
53162306a36Sopenharmony_ci		break;
53262306a36Sopenharmony_ci	default:
53362306a36Sopenharmony_ci		return -EINVAL;
53462306a36Sopenharmony_ci	}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	ret = wait_for_ip_sleep(ssusb);
53762306a36Sopenharmony_ci	if (ret)
53862306a36Sopenharmony_ci		goto sleep_err;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	ssusb_phy_power_off(ssusb);
54162306a36Sopenharmony_ci	clk_bulk_disable_unprepare(BULK_CLKS_CNT, ssusb->clks);
54262306a36Sopenharmony_ci	ssusb_wakeup_set(ssusb, true);
54362306a36Sopenharmony_ci	return 0;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_cisleep_err:
54662306a36Sopenharmony_ci	resume_ip_and_ports(ssusb, msg);
54762306a36Sopenharmony_cierr:
54862306a36Sopenharmony_ci	return ret;
54962306a36Sopenharmony_ci}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_cistatic int mtu3_resume_common(struct device *dev, pm_message_t msg)
55262306a36Sopenharmony_ci{
55362306a36Sopenharmony_ci	struct ssusb_mtk *ssusb = dev_get_drvdata(dev);
55462306a36Sopenharmony_ci	int ret;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	dev_dbg(dev, "%s\n", __func__);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	ssusb_wakeup_set(ssusb, false);
55962306a36Sopenharmony_ci	ret = clk_bulk_prepare_enable(BULK_CLKS_CNT, ssusb->clks);
56062306a36Sopenharmony_ci	if (ret)
56162306a36Sopenharmony_ci		goto clks_err;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	ret = ssusb_phy_power_on(ssusb);
56462306a36Sopenharmony_ci	if (ret)
56562306a36Sopenharmony_ci		goto phy_err;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	return resume_ip_and_ports(ssusb, msg);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ciphy_err:
57062306a36Sopenharmony_ci	clk_bulk_disable_unprepare(BULK_CLKS_CNT, ssusb->clks);
57162306a36Sopenharmony_ciclks_err:
57262306a36Sopenharmony_ci	return ret;
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_cistatic int __maybe_unused mtu3_suspend(struct device *dev)
57662306a36Sopenharmony_ci{
57762306a36Sopenharmony_ci	return mtu3_suspend_common(dev, PMSG_SUSPEND);
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic int __maybe_unused mtu3_resume(struct device *dev)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	return mtu3_resume_common(dev, PMSG_SUSPEND);
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_cistatic int __maybe_unused mtu3_runtime_suspend(struct device *dev)
58662306a36Sopenharmony_ci{
58762306a36Sopenharmony_ci	if (!device_may_wakeup(dev))
58862306a36Sopenharmony_ci		return 0;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	return mtu3_suspend_common(dev, PMSG_AUTO_SUSPEND);
59162306a36Sopenharmony_ci}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_cistatic int __maybe_unused mtu3_runtime_resume(struct device *dev)
59462306a36Sopenharmony_ci{
59562306a36Sopenharmony_ci	if (!device_may_wakeup(dev))
59662306a36Sopenharmony_ci		return 0;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	return mtu3_resume_common(dev, PMSG_AUTO_SUSPEND);
59962306a36Sopenharmony_ci}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_cistatic const struct dev_pm_ops mtu3_pm_ops = {
60262306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(mtu3_suspend, mtu3_resume)
60362306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(mtu3_runtime_suspend,
60462306a36Sopenharmony_ci			   mtu3_runtime_resume, NULL)
60562306a36Sopenharmony_ci};
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci#define DEV_PM_OPS (IS_ENABLED(CONFIG_PM) ? &mtu3_pm_ops : NULL)
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_cistatic const struct of_device_id mtu3_of_match[] = {
61062306a36Sopenharmony_ci	{.compatible = "mediatek,mt8173-mtu3",},
61162306a36Sopenharmony_ci	{.compatible = "mediatek,mtu3",},
61262306a36Sopenharmony_ci	{},
61362306a36Sopenharmony_ci};
61462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mtu3_of_match);
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_cistatic struct platform_driver mtu3_driver = {
61762306a36Sopenharmony_ci	.probe = mtu3_probe,
61862306a36Sopenharmony_ci	.remove = mtu3_remove,
61962306a36Sopenharmony_ci	.driver = {
62062306a36Sopenharmony_ci		.name = MTU3_DRIVER_NAME,
62162306a36Sopenharmony_ci		.pm = DEV_PM_OPS,
62262306a36Sopenharmony_ci		.of_match_table = mtu3_of_match,
62362306a36Sopenharmony_ci	},
62462306a36Sopenharmony_ci};
62562306a36Sopenharmony_cimodule_platform_driver(mtu3_driver);
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ciMODULE_AUTHOR("Chunfeng Yun <chunfeng.yun@mediatek.com>");
62862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
62962306a36Sopenharmony_ciMODULE_DESCRIPTION("MediaTek USB3 DRD Controller Driver");
630