Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c776f2c3 authored by Chunfeng Yun's avatar Chunfeng Yun Committed by Felipe Balbi
Browse files

usb: mtu3: use FORCE/RG_IDDIG to implement manual DRD switch



In order to keep manual DRD switch independent on IDDIG interrupt,
make use of FORCE/RG_IDDIG instead of IDDIG EINT interrupt to
implement manual DRD switch function.

Signed-off-by: default avatarChunfeng Yun <chunfeng.yun@mediatek.com>
Signed-off-by: default avatarFelipe Balbi <felipe.balbi@linux.intel.com>
parent 1a46dfea
Loading
Loading
Loading
Loading
+13 −5
Original line number Diff line number Diff line
@@ -114,6 +114,19 @@ enum mtu3_g_ep0_state {
	MU3D_EP0_STATE_STALL,
};

/**
 * MTU3_DR_FORCE_NONE: automatically switch host and periperal mode
 *		by IDPIN signal.
 * MTU3_DR_FORCE_HOST: force to enter host mode and override OTG
 *		IDPIN signal.
 * MTU3_DR_FORCE_DEVICE: force to enter peripheral mode.
 */
enum mtu3_dr_force_mode {
	MTU3_DR_FORCE_NONE = 0,
	MTU3_DR_FORCE_HOST,
	MTU3_DR_FORCE_DEVICE,
};

/**
 * @base: the base address of fifo
 * @limit: the bitmap size in bits
@@ -196,7 +209,6 @@ struct mtu3_gpd_ring {
*		xHCI driver initialization, it's necessary for system bootup
*		as device.
* @is_u3_drd: whether port0 supports usb3.0 dual-role device or not
* @id_*: used to maually switch between host and device modes by idpin
* @manual_drd_enabled: it's true when supports dual-role device by debugfs
*		to switch host/device modes depending on user input.
*/
@@ -207,10 +219,6 @@ struct otg_switch_mtk {
	struct notifier_block id_nb;
	struct delayed_work extcon_reg_dwork;
	bool is_u3_drd;
	/* dual-role switch by debugfs */
	struct pinctrl *id_pinctrl;
	struct pinctrl_state *id_float;
	struct pinctrl_state *id_ground;
	bool manual_drd_enabled;
};

+45 −16
Original line number Diff line number Diff line
@@ -261,20 +261,21 @@ static void extcon_register_dwork(struct work_struct *work)
 * depending on user input.
 * This is useful in special cases, such as uses TYPE-A receptacle but also
 * wants to support dual-role mode.
 * It generates cable state changes by pulling up/down IDPIN and
 * notifies driver to switch mode by "extcon-usb-gpio".
 * NOTE: when use MICRO receptacle, should not enable this interface.
 */
static void ssusb_mode_manual_switch(struct ssusb_mtk *ssusb, int to_host)
{
	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;

	if (to_host)
		pinctrl_select_state(otg_sx->id_pinctrl, otg_sx->id_ground);
	else
		pinctrl_select_state(otg_sx->id_pinctrl, otg_sx->id_float);
	if (to_host) {
		ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_HOST);
		ssusb_set_mailbox(otg_sx, MTU3_VBUS_OFF);
		ssusb_set_mailbox(otg_sx, MTU3_ID_GROUND);
	} else {
		ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_DEVICE);
		ssusb_set_mailbox(otg_sx, MTU3_ID_FLOAT);
		ssusb_set_mailbox(otg_sx, MTU3_VBUS_VALID);
	}
}


static int ssusb_mode_show(struct seq_file *sf, void *unused)
{
@@ -388,17 +389,45 @@ static void ssusb_debugfs_exit(struct ssusb_mtk *ssusb)
	debugfs_remove_recursive(ssusb->dbgfs_root);
}

void ssusb_set_force_mode(struct ssusb_mtk *ssusb,
			  enum mtu3_dr_force_mode mode)
{
	u32 value;

	value = mtu3_readl(ssusb->ippc_base, SSUSB_U2_CTRL(0));
	switch (mode) {
	case MTU3_DR_FORCE_DEVICE:
		value |= SSUSB_U2_PORT_FORCE_IDDIG | SSUSB_U2_PORT_RG_IDDIG;
		break;
	case MTU3_DR_FORCE_HOST:
		value |= SSUSB_U2_PORT_FORCE_IDDIG;
		value &= ~SSUSB_U2_PORT_RG_IDDIG;
		break;
	case MTU3_DR_FORCE_NONE:
		value &= ~(SSUSB_U2_PORT_FORCE_IDDIG | SSUSB_U2_PORT_RG_IDDIG);
		break;
	default:
		return;
	}
	mtu3_writel(ssusb->ippc_base, SSUSB_U2_CTRL(0), value);
}

int ssusb_otg_switch_init(struct ssusb_mtk *ssusb)
{
	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;

	INIT_DELAYED_WORK(&otg_sx->extcon_reg_dwork, extcon_register_dwork);

	if (otg_sx->manual_drd_enabled)
	if (otg_sx->manual_drd_enabled) {
		ssusb_debugfs_init(ssusb);
	} else {
		INIT_DELAYED_WORK(&otg_sx->extcon_reg_dwork,
				  extcon_register_dwork);

	/* It is enough to delay 1s for waiting for host initialization */
		/*
		 * It is enough to delay 1s for waiting for
		 * host initialization
		 */
		schedule_delayed_work(&otg_sx->extcon_reg_dwork, HZ);
	}

	return 0;
}
@@ -407,8 +436,8 @@ void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb)
{
	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;

	cancel_delayed_work(&otg_sx->extcon_reg_dwork);

	if (otg_sx->manual_drd_enabled)
		ssusb_debugfs_exit(ssusb);
	else
		cancel_delayed_work(&otg_sx->extcon_reg_dwork);
}
+6 −0
Original line number Diff line number Diff line
@@ -87,6 +87,8 @@ static inline void ssusb_gadget_exit(struct ssusb_mtk *ssusb)
int ssusb_otg_switch_init(struct ssusb_mtk *ssusb);
void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb);
int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on);
void ssusb_set_force_mode(struct ssusb_mtk *ssusb,
			  enum mtu3_dr_force_mode mode);

#else

@@ -103,6 +105,10 @@ static inline int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on)
	return 0;
}

static inline void
ssusb_set_force_mode(struct ssusb_mtk *ssusb, enum mtu3_dr_force_mode mode)
{}

#endif

#endif		/* _MTU3_DR_H_ */
+5 −0
Original line number Diff line number Diff line
@@ -189,6 +189,8 @@ int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend)

static void ssusb_host_setup(struct ssusb_mtk *ssusb)
{
	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;

	host_ports_num_get(ssusb);

	/*
@@ -197,6 +199,9 @@ static void ssusb_host_setup(struct ssusb_mtk *ssusb)
	 */
	ssusb_host_enable(ssusb);

	if (otg_sx->manual_drd_enabled)
		ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_HOST);

	/* if port0 supports dual-role, works as host mode by default */
	ssusb_set_vbus(&ssusb->otg_switch, 1);
}
+2 −0
Original line number Diff line number Diff line
@@ -472,6 +472,8 @@
#define SSUSB_U3_PORT_DIS		BIT(0)

/* U3D_SSUSB_U2_CTRL_0P */
#define SSUSB_U2_PORT_RG_IDDIG		BIT(12)
#define SSUSB_U2_PORT_FORCE_IDDIG	BIT(11)
#define SSUSB_U2_PORT_VBUSVALID	BIT(9)
#define SSUSB_U2_PORT_OTG_SEL		BIT(7)
#define SSUSB_U2_PORT_HOST		BIT(2)
Loading