18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * mtu3_dr.c - dual role switch and host glue layer
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2016 MediaTek Inc.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/usb/role.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "mtu3.h"
138c2ecf20Sopenharmony_ci#include "mtu3_dr.h"
148c2ecf20Sopenharmony_ci#include "mtu3_debug.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define USB2_PORT 2
178c2ecf20Sopenharmony_ci#define USB3_PORT 3
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cienum mtu3_vbus_id_state {
208c2ecf20Sopenharmony_ci	MTU3_ID_FLOAT = 1,
218c2ecf20Sopenharmony_ci	MTU3_ID_GROUND,
228c2ecf20Sopenharmony_ci	MTU3_VBUS_OFF,
238c2ecf20Sopenharmony_ci	MTU3_VBUS_VALID,
248c2ecf20Sopenharmony_ci};
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic char *mailbox_state_string(enum mtu3_vbus_id_state state)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	switch (state) {
298c2ecf20Sopenharmony_ci	case MTU3_ID_FLOAT:
308c2ecf20Sopenharmony_ci		return "ID_FLOAT";
318c2ecf20Sopenharmony_ci	case MTU3_ID_GROUND:
328c2ecf20Sopenharmony_ci		return "ID_GROUND";
338c2ecf20Sopenharmony_ci	case MTU3_VBUS_OFF:
348c2ecf20Sopenharmony_ci		return "VBUS_OFF";
358c2ecf20Sopenharmony_ci	case MTU3_VBUS_VALID:
368c2ecf20Sopenharmony_ci		return "VBUS_VALID";
378c2ecf20Sopenharmony_ci	default:
388c2ecf20Sopenharmony_ci		return "UNKNOWN";
398c2ecf20Sopenharmony_ci	}
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic void toggle_opstate(struct ssusb_mtk *ssusb)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	mtu3_setbits(ssusb->mac_base, U3D_DEVICE_CONTROL, DC_SESSION);
458c2ecf20Sopenharmony_ci	mtu3_setbits(ssusb->mac_base, U3D_POWER_MANAGEMENT, SOFT_CONN);
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/* only port0 supports dual-role mode */
498c2ecf20Sopenharmony_cistatic int ssusb_port0_switch(struct ssusb_mtk *ssusb,
508c2ecf20Sopenharmony_ci	int version, bool tohost)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	void __iomem *ibase = ssusb->ippc_base;
538c2ecf20Sopenharmony_ci	u32 value;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	dev_dbg(ssusb->dev, "%s (switch u%d port0 to %s)\n", __func__,
568c2ecf20Sopenharmony_ci		version, tohost ? "host" : "device");
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	if (version == USB2_PORT) {
598c2ecf20Sopenharmony_ci		/* 1. power off and disable u2 port0 */
608c2ecf20Sopenharmony_ci		value = mtu3_readl(ibase, SSUSB_U2_CTRL(0));
618c2ecf20Sopenharmony_ci		value |= SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS;
628c2ecf20Sopenharmony_ci		mtu3_writel(ibase, SSUSB_U2_CTRL(0), value);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci		/* 2. power on, enable u2 port0 and select its mode */
658c2ecf20Sopenharmony_ci		value = mtu3_readl(ibase, SSUSB_U2_CTRL(0));
668c2ecf20Sopenharmony_ci		value &= ~(SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS);
678c2ecf20Sopenharmony_ci		value = tohost ? (value | SSUSB_U2_PORT_HOST_SEL) :
688c2ecf20Sopenharmony_ci			(value & (~SSUSB_U2_PORT_HOST_SEL));
698c2ecf20Sopenharmony_ci		mtu3_writel(ibase, SSUSB_U2_CTRL(0), value);
708c2ecf20Sopenharmony_ci	} else {
718c2ecf20Sopenharmony_ci		/* 1. power off and disable u3 port0 */
728c2ecf20Sopenharmony_ci		value = mtu3_readl(ibase, SSUSB_U3_CTRL(0));
738c2ecf20Sopenharmony_ci		value |= SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS;
748c2ecf20Sopenharmony_ci		mtu3_writel(ibase, SSUSB_U3_CTRL(0), value);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci		/* 2. power on, enable u3 port0 and select its mode */
778c2ecf20Sopenharmony_ci		value = mtu3_readl(ibase, SSUSB_U3_CTRL(0));
788c2ecf20Sopenharmony_ci		value &= ~(SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS);
798c2ecf20Sopenharmony_ci		value = tohost ? (value | SSUSB_U3_PORT_HOST_SEL) :
808c2ecf20Sopenharmony_ci			(value & (~SSUSB_U3_PORT_HOST_SEL));
818c2ecf20Sopenharmony_ci		mtu3_writel(ibase, SSUSB_U3_CTRL(0), value);
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	return 0;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic void switch_port_to_host(struct ssusb_mtk *ssusb)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	u32 check_clk = 0;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	dev_dbg(ssusb->dev, "%s\n", __func__);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	ssusb_port0_switch(ssusb, USB2_PORT, true);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (ssusb->otg_switch.is_u3_drd) {
968c2ecf20Sopenharmony_ci		ssusb_port0_switch(ssusb, USB3_PORT, true);
978c2ecf20Sopenharmony_ci		check_clk = SSUSB_U3_MAC_RST_B_STS;
988c2ecf20Sopenharmony_ci	}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	ssusb_check_clocks(ssusb, check_clk);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	/* after all clocks are stable */
1038c2ecf20Sopenharmony_ci	toggle_opstate(ssusb);
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic void switch_port_to_device(struct ssusb_mtk *ssusb)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	u32 check_clk = 0;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	dev_dbg(ssusb->dev, "%s\n", __func__);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	ssusb_port0_switch(ssusb, USB2_PORT, false);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	if (ssusb->otg_switch.is_u3_drd) {
1158c2ecf20Sopenharmony_ci		ssusb_port0_switch(ssusb, USB3_PORT, false);
1168c2ecf20Sopenharmony_ci		check_clk = SSUSB_U3_MAC_RST_B_STS;
1178c2ecf20Sopenharmony_ci	}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	ssusb_check_clocks(ssusb, check_clk);
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ciint ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	struct ssusb_mtk *ssusb =
1258c2ecf20Sopenharmony_ci		container_of(otg_sx, struct ssusb_mtk, otg_switch);
1268c2ecf20Sopenharmony_ci	struct regulator *vbus = otg_sx->vbus;
1278c2ecf20Sopenharmony_ci	int ret;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	/* vbus is optional */
1308c2ecf20Sopenharmony_ci	if (!vbus)
1318c2ecf20Sopenharmony_ci		return 0;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	dev_dbg(ssusb->dev, "%s: turn %s\n", __func__, is_on ? "on" : "off");
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	if (is_on) {
1368c2ecf20Sopenharmony_ci		ret = regulator_enable(vbus);
1378c2ecf20Sopenharmony_ci		if (ret) {
1388c2ecf20Sopenharmony_ci			dev_err(ssusb->dev, "vbus regulator enable failed\n");
1398c2ecf20Sopenharmony_ci			return ret;
1408c2ecf20Sopenharmony_ci		}
1418c2ecf20Sopenharmony_ci	} else {
1428c2ecf20Sopenharmony_ci		regulator_disable(vbus);
1438c2ecf20Sopenharmony_ci	}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	return 0;
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci/*
1498c2ecf20Sopenharmony_ci * switch to host: -> MTU3_VBUS_OFF --> MTU3_ID_GROUND
1508c2ecf20Sopenharmony_ci * switch to device: -> MTU3_ID_FLOAT --> MTU3_VBUS_VALID
1518c2ecf20Sopenharmony_ci */
1528c2ecf20Sopenharmony_cistatic void ssusb_set_mailbox(struct otg_switch_mtk *otg_sx,
1538c2ecf20Sopenharmony_ci	enum mtu3_vbus_id_state status)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	struct ssusb_mtk *ssusb =
1568c2ecf20Sopenharmony_ci		container_of(otg_sx, struct ssusb_mtk, otg_switch);
1578c2ecf20Sopenharmony_ci	struct mtu3 *mtu = ssusb->u3d;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	dev_dbg(ssusb->dev, "mailbox %s\n", mailbox_state_string(status));
1608c2ecf20Sopenharmony_ci	mtu3_dbg_trace(ssusb->dev, "mailbox %s", mailbox_state_string(status));
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	switch (status) {
1638c2ecf20Sopenharmony_ci	case MTU3_ID_GROUND:
1648c2ecf20Sopenharmony_ci		switch_port_to_host(ssusb);
1658c2ecf20Sopenharmony_ci		ssusb_set_vbus(otg_sx, 1);
1668c2ecf20Sopenharmony_ci		ssusb->is_host = true;
1678c2ecf20Sopenharmony_ci		break;
1688c2ecf20Sopenharmony_ci	case MTU3_ID_FLOAT:
1698c2ecf20Sopenharmony_ci		ssusb->is_host = false;
1708c2ecf20Sopenharmony_ci		ssusb_set_vbus(otg_sx, 0);
1718c2ecf20Sopenharmony_ci		switch_port_to_device(ssusb);
1728c2ecf20Sopenharmony_ci		break;
1738c2ecf20Sopenharmony_ci	case MTU3_VBUS_OFF:
1748c2ecf20Sopenharmony_ci		mtu3_stop(mtu);
1758c2ecf20Sopenharmony_ci		pm_relax(ssusb->dev);
1768c2ecf20Sopenharmony_ci		break;
1778c2ecf20Sopenharmony_ci	case MTU3_VBUS_VALID:
1788c2ecf20Sopenharmony_ci		/* avoid suspend when works as device */
1798c2ecf20Sopenharmony_ci		pm_stay_awake(ssusb->dev);
1808c2ecf20Sopenharmony_ci		mtu3_start(mtu);
1818c2ecf20Sopenharmony_ci		break;
1828c2ecf20Sopenharmony_ci	default:
1838c2ecf20Sopenharmony_ci		dev_err(ssusb->dev, "invalid state\n");
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic void ssusb_id_work(struct work_struct *work)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	struct otg_switch_mtk *otg_sx =
1908c2ecf20Sopenharmony_ci		container_of(work, struct otg_switch_mtk, id_work);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	if (otg_sx->id_event)
1938c2ecf20Sopenharmony_ci		ssusb_set_mailbox(otg_sx, MTU3_ID_GROUND);
1948c2ecf20Sopenharmony_ci	else
1958c2ecf20Sopenharmony_ci		ssusb_set_mailbox(otg_sx, MTU3_ID_FLOAT);
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic void ssusb_vbus_work(struct work_struct *work)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	struct otg_switch_mtk *otg_sx =
2018c2ecf20Sopenharmony_ci		container_of(work, struct otg_switch_mtk, vbus_work);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	if (otg_sx->vbus_event)
2048c2ecf20Sopenharmony_ci		ssusb_set_mailbox(otg_sx, MTU3_VBUS_VALID);
2058c2ecf20Sopenharmony_ci	else
2068c2ecf20Sopenharmony_ci		ssusb_set_mailbox(otg_sx, MTU3_VBUS_OFF);
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci/*
2108c2ecf20Sopenharmony_ci * @ssusb_id_notifier is called in atomic context, but @ssusb_set_mailbox
2118c2ecf20Sopenharmony_ci * may sleep, so use work queue here
2128c2ecf20Sopenharmony_ci */
2138c2ecf20Sopenharmony_cistatic int ssusb_id_notifier(struct notifier_block *nb,
2148c2ecf20Sopenharmony_ci	unsigned long event, void *ptr)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	struct otg_switch_mtk *otg_sx =
2178c2ecf20Sopenharmony_ci		container_of(nb, struct otg_switch_mtk, id_nb);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	otg_sx->id_event = event;
2208c2ecf20Sopenharmony_ci	schedule_work(&otg_sx->id_work);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic int ssusb_vbus_notifier(struct notifier_block *nb,
2268c2ecf20Sopenharmony_ci	unsigned long event, void *ptr)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	struct otg_switch_mtk *otg_sx =
2298c2ecf20Sopenharmony_ci		container_of(nb, struct otg_switch_mtk, vbus_nb);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	otg_sx->vbus_event = event;
2328c2ecf20Sopenharmony_ci	schedule_work(&otg_sx->vbus_work);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic int ssusb_extcon_register(struct otg_switch_mtk *otg_sx)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	struct ssusb_mtk *ssusb =
2408c2ecf20Sopenharmony_ci		container_of(otg_sx, struct ssusb_mtk, otg_switch);
2418c2ecf20Sopenharmony_ci	struct extcon_dev *edev = otg_sx->edev;
2428c2ecf20Sopenharmony_ci	int ret;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	/* extcon is optional */
2458c2ecf20Sopenharmony_ci	if (!edev)
2468c2ecf20Sopenharmony_ci		return 0;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	otg_sx->vbus_nb.notifier_call = ssusb_vbus_notifier;
2498c2ecf20Sopenharmony_ci	ret = devm_extcon_register_notifier(ssusb->dev, edev, EXTCON_USB,
2508c2ecf20Sopenharmony_ci					&otg_sx->vbus_nb);
2518c2ecf20Sopenharmony_ci	if (ret < 0) {
2528c2ecf20Sopenharmony_ci		dev_err(ssusb->dev, "failed to register notifier for USB\n");
2538c2ecf20Sopenharmony_ci		return ret;
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	otg_sx->id_nb.notifier_call = ssusb_id_notifier;
2578c2ecf20Sopenharmony_ci	ret = devm_extcon_register_notifier(ssusb->dev, edev, EXTCON_USB_HOST,
2588c2ecf20Sopenharmony_ci					&otg_sx->id_nb);
2598c2ecf20Sopenharmony_ci	if (ret < 0) {
2608c2ecf20Sopenharmony_ci		dev_err(ssusb->dev, "failed to register notifier for USB-HOST\n");
2618c2ecf20Sopenharmony_ci		return ret;
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	dev_dbg(ssusb->dev, "EXTCON_USB: %d, EXTCON_USB_HOST: %d\n",
2658c2ecf20Sopenharmony_ci		extcon_get_state(edev, EXTCON_USB),
2668c2ecf20Sopenharmony_ci		extcon_get_state(edev, EXTCON_USB_HOST));
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	/* default as host, switch to device mode if needed */
2698c2ecf20Sopenharmony_ci	if (extcon_get_state(edev, EXTCON_USB_HOST) == false)
2708c2ecf20Sopenharmony_ci		ssusb_set_mailbox(otg_sx, MTU3_ID_FLOAT);
2718c2ecf20Sopenharmony_ci	if (extcon_get_state(edev, EXTCON_USB) == true)
2728c2ecf20Sopenharmony_ci		ssusb_set_mailbox(otg_sx, MTU3_VBUS_VALID);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	return 0;
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci/*
2788c2ecf20Sopenharmony_ci * We provide an interface via debugfs to switch between host and device modes
2798c2ecf20Sopenharmony_ci * depending on user input.
2808c2ecf20Sopenharmony_ci * This is useful in special cases, such as uses TYPE-A receptacle but also
2818c2ecf20Sopenharmony_ci * wants to support dual-role mode.
2828c2ecf20Sopenharmony_ci */
2838c2ecf20Sopenharmony_civoid ssusb_mode_switch(struct ssusb_mtk *ssusb, int to_host)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	if (to_host) {
2888c2ecf20Sopenharmony_ci		ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_HOST);
2898c2ecf20Sopenharmony_ci		ssusb_set_mailbox(otg_sx, MTU3_VBUS_OFF);
2908c2ecf20Sopenharmony_ci		ssusb_set_mailbox(otg_sx, MTU3_ID_GROUND);
2918c2ecf20Sopenharmony_ci	} else {
2928c2ecf20Sopenharmony_ci		ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_DEVICE);
2938c2ecf20Sopenharmony_ci		ssusb_set_mailbox(otg_sx, MTU3_ID_FLOAT);
2948c2ecf20Sopenharmony_ci		ssusb_set_mailbox(otg_sx, MTU3_VBUS_VALID);
2958c2ecf20Sopenharmony_ci	}
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_civoid ssusb_set_force_mode(struct ssusb_mtk *ssusb,
2998c2ecf20Sopenharmony_ci			  enum mtu3_dr_force_mode mode)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	u32 value;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	value = mtu3_readl(ssusb->ippc_base, SSUSB_U2_CTRL(0));
3048c2ecf20Sopenharmony_ci	switch (mode) {
3058c2ecf20Sopenharmony_ci	case MTU3_DR_FORCE_DEVICE:
3068c2ecf20Sopenharmony_ci		value |= SSUSB_U2_PORT_FORCE_IDDIG | SSUSB_U2_PORT_RG_IDDIG;
3078c2ecf20Sopenharmony_ci		break;
3088c2ecf20Sopenharmony_ci	case MTU3_DR_FORCE_HOST:
3098c2ecf20Sopenharmony_ci		value |= SSUSB_U2_PORT_FORCE_IDDIG;
3108c2ecf20Sopenharmony_ci		value &= ~SSUSB_U2_PORT_RG_IDDIG;
3118c2ecf20Sopenharmony_ci		break;
3128c2ecf20Sopenharmony_ci	case MTU3_DR_FORCE_NONE:
3138c2ecf20Sopenharmony_ci		value &= ~(SSUSB_U2_PORT_FORCE_IDDIG | SSUSB_U2_PORT_RG_IDDIG);
3148c2ecf20Sopenharmony_ci		break;
3158c2ecf20Sopenharmony_ci	default:
3168c2ecf20Sopenharmony_ci		return;
3178c2ecf20Sopenharmony_ci	}
3188c2ecf20Sopenharmony_ci	mtu3_writel(ssusb->ippc_base, SSUSB_U2_CTRL(0), value);
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic int ssusb_role_sw_set(struct usb_role_switch *sw, enum usb_role role)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	struct ssusb_mtk *ssusb = usb_role_switch_get_drvdata(sw);
3248c2ecf20Sopenharmony_ci	bool to_host = false;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	if (role == USB_ROLE_HOST)
3278c2ecf20Sopenharmony_ci		to_host = true;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	if (to_host ^ ssusb->is_host)
3308c2ecf20Sopenharmony_ci		ssusb_mode_switch(ssusb, to_host);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	return 0;
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic enum usb_role ssusb_role_sw_get(struct usb_role_switch *sw)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	struct ssusb_mtk *ssusb = usb_role_switch_get_drvdata(sw);
3388c2ecf20Sopenharmony_ci	enum usb_role role;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	role = ssusb->is_host ? USB_ROLE_HOST : USB_ROLE_DEVICE;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	return role;
3438c2ecf20Sopenharmony_ci}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_cistatic int ssusb_role_sw_register(struct otg_switch_mtk *otg_sx)
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	struct usb_role_switch_desc role_sx_desc = { 0 };
3488c2ecf20Sopenharmony_ci	struct ssusb_mtk *ssusb =
3498c2ecf20Sopenharmony_ci		container_of(otg_sx, struct ssusb_mtk, otg_switch);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	if (!otg_sx->role_sw_used)
3528c2ecf20Sopenharmony_ci		return 0;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	role_sx_desc.set = ssusb_role_sw_set;
3558c2ecf20Sopenharmony_ci	role_sx_desc.get = ssusb_role_sw_get;
3568c2ecf20Sopenharmony_ci	role_sx_desc.fwnode = dev_fwnode(ssusb->dev);
3578c2ecf20Sopenharmony_ci	role_sx_desc.driver_data = ssusb;
3588c2ecf20Sopenharmony_ci	otg_sx->role_sw = usb_role_switch_register(ssusb->dev, &role_sx_desc);
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	return PTR_ERR_OR_ZERO(otg_sx->role_sw);
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ciint ssusb_otg_switch_init(struct ssusb_mtk *ssusb)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
3668c2ecf20Sopenharmony_ci	int ret = 0;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	INIT_WORK(&otg_sx->id_work, ssusb_id_work);
3698c2ecf20Sopenharmony_ci	INIT_WORK(&otg_sx->vbus_work, ssusb_vbus_work);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	if (otg_sx->manual_drd_enabled)
3728c2ecf20Sopenharmony_ci		ssusb_dr_debugfs_init(ssusb);
3738c2ecf20Sopenharmony_ci	else if (otg_sx->role_sw_used)
3748c2ecf20Sopenharmony_ci		ret = ssusb_role_sw_register(otg_sx);
3758c2ecf20Sopenharmony_ci	else
3768c2ecf20Sopenharmony_ci		ret = ssusb_extcon_register(otg_sx);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	return ret;
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_civoid ssusb_otg_switch_exit(struct ssusb_mtk *ssusb)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	cancel_work_sync(&otg_sx->id_work);
3868c2ecf20Sopenharmony_ci	cancel_work_sync(&otg_sx->vbus_work);
3878c2ecf20Sopenharmony_ci	usb_role_switch_unregister(otg_sx->role_sw);
3888c2ecf20Sopenharmony_ci}
389