Skip to content

Commit

Permalink
usb: mtu3: use FORCE/RG_IDDIG to implement manual DRD switch
Browse files Browse the repository at this point in the history
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: Chunfeng Yun <chunfeng.yun@mediatek.com>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
  • Loading branch information
Chunfeng Yun authored and Felipe Balbi committed Oct 19, 2017
1 parent 1a46dfe commit c776f2c
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 56 deletions.
18 changes: 13 additions & 5 deletions drivers/usb/mtu3/mtu3.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
*/
Expand All @@ -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;
};

Expand Down
61 changes: 45 additions & 16 deletions drivers/usb/mtu3/mtu3_dr.c
Original file line number Diff line number Diff line change
Expand Up @@ -261,21 +261,22 @@ 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)
{
struct ssusb_mtk *ssusb = sf->private;
Expand Down Expand Up @@ -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);

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

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

return 0;
}
Expand All @@ -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 changes: 6 additions & 0 deletions drivers/usb/mtu3/mtu3_dr.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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 changes: 5 additions & 0 deletions drivers/usb/mtu3/mtu3_host.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

/*
Expand All @@ -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);
}
Expand Down
2 changes: 2 additions & 0 deletions drivers/usb/mtu3/mtu3_hw_regs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
38 changes: 3 additions & 35 deletions drivers/usb/mtu3/mtu3_plat.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>

#include "mtu3.h"
Expand Down Expand Up @@ -212,33 +211,6 @@ static void ssusb_ip_sw_reset(struct ssusb_mtk *ssusb)
mtu3_clrbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST);
}

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

otg_sx->id_pinctrl = devm_pinctrl_get(ssusb->dev);
if (IS_ERR(otg_sx->id_pinctrl)) {
dev_err(ssusb->dev, "Cannot find id pinctrl!\n");
return PTR_ERR(otg_sx->id_pinctrl);
}

otg_sx->id_float =
pinctrl_lookup_state(otg_sx->id_pinctrl, "id_float");
if (IS_ERR(otg_sx->id_float)) {
dev_err(ssusb->dev, "Cannot find pinctrl id_float!\n");
return PTR_ERR(otg_sx->id_float);
}

otg_sx->id_ground =
pinctrl_lookup_state(otg_sx->id_pinctrl, "id_ground");
if (IS_ERR(otg_sx->id_ground)) {
dev_err(ssusb->dev, "Cannot find pinctrl id_ground!\n");
return PTR_ERR(otg_sx->id_ground);
}

return 0;
}

/* ignore the error if the clock does not exist */
static struct clk *get_optional_clk(struct device *dev, const char *id)
{
Expand Down Expand Up @@ -349,15 +321,11 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb)
dev_err(ssusb->dev, "couldn't get extcon device\n");
return -EPROBE_DEFER;
}
if (otg_sx->manual_drd_enabled) {
ret = get_iddig_pinctrl(ssusb);
if (ret)
return ret;
}
}

dev_info(dev, "dr_mode: %d, is_u3_dr: %d, u3p_dis_msk:%x\n",
ssusb->dr_mode, otg_sx->is_u3_drd, ssusb->u3p_dis_msk);
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,
otg_sx->manual_drd_enabled ? "manual" : "auto");

return 0;
}
Expand Down

0 comments on commit c776f2c

Please sign in to comment.