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: Chunfeng Yun <chunfeng.yun@mediatek.com> Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
This commit is contained in:
parent
1a46dfea08
commit
c776f2c3e8
|
@ -114,6 +114,19 @@ enum mtu3_g_ep0_state {
|
||||||
MU3D_EP0_STATE_STALL,
|
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
|
* @base: the base address of fifo
|
||||||
* @limit: the bitmap size in bits
|
* @limit: the bitmap size in bits
|
||||||
|
@ -196,7 +209,6 @@ struct mtu3_gpd_ring {
|
||||||
* xHCI driver initialization, it's necessary for system bootup
|
* xHCI driver initialization, it's necessary for system bootup
|
||||||
* as device.
|
* as device.
|
||||||
* @is_u3_drd: whether port0 supports usb3.0 dual-role device or not
|
* @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
|
* @manual_drd_enabled: it's true when supports dual-role device by debugfs
|
||||||
* to switch host/device modes depending on user input.
|
* to switch host/device modes depending on user input.
|
||||||
*/
|
*/
|
||||||
|
@ -207,10 +219,6 @@ struct otg_switch_mtk {
|
||||||
struct notifier_block id_nb;
|
struct notifier_block id_nb;
|
||||||
struct delayed_work extcon_reg_dwork;
|
struct delayed_work extcon_reg_dwork;
|
||||||
bool is_u3_drd;
|
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;
|
bool manual_drd_enabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -261,21 +261,22 @@ static void extcon_register_dwork(struct work_struct *work)
|
||||||
* depending on user input.
|
* depending on user input.
|
||||||
* This is useful in special cases, such as uses TYPE-A receptacle but also
|
* This is useful in special cases, such as uses TYPE-A receptacle but also
|
||||||
* wants to support dual-role mode.
|
* 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)
|
static void ssusb_mode_manual_switch(struct ssusb_mtk *ssusb, int to_host)
|
||||||
{
|
{
|
||||||
struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
|
struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
|
||||||
|
|
||||||
if (to_host)
|
if (to_host) {
|
||||||
pinctrl_select_state(otg_sx->id_pinctrl, otg_sx->id_ground);
|
ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_HOST);
|
||||||
else
|
ssusb_set_mailbox(otg_sx, MTU3_VBUS_OFF);
|
||||||
pinctrl_select_state(otg_sx->id_pinctrl, otg_sx->id_float);
|
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)
|
static int ssusb_mode_show(struct seq_file *sf, void *unused)
|
||||||
{
|
{
|
||||||
struct ssusb_mtk *ssusb = sf->private;
|
struct ssusb_mtk *ssusb = sf->private;
|
||||||
|
@ -388,17 +389,45 @@ static void ssusb_debugfs_exit(struct ssusb_mtk *ssusb)
|
||||||
debugfs_remove_recursive(ssusb->dbgfs_root);
|
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)
|
int ssusb_otg_switch_init(struct ssusb_mtk *ssusb)
|
||||||
{
|
{
|
||||||
struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
|
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);
|
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 */
|
/*
|
||||||
schedule_delayed_work(&otg_sx->extcon_reg_dwork, HZ);
|
* It is enough to delay 1s for waiting for
|
||||||
|
* host initialization
|
||||||
|
*/
|
||||||
|
schedule_delayed_work(&otg_sx->extcon_reg_dwork, HZ);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -407,8 +436,8 @@ void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb)
|
||||||
{
|
{
|
||||||
struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
|
struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
|
||||||
|
|
||||||
cancel_delayed_work(&otg_sx->extcon_reg_dwork);
|
|
||||||
|
|
||||||
if (otg_sx->manual_drd_enabled)
|
if (otg_sx->manual_drd_enabled)
|
||||||
ssusb_debugfs_exit(ssusb);
|
ssusb_debugfs_exit(ssusb);
|
||||||
|
else
|
||||||
|
cancel_delayed_work(&otg_sx->extcon_reg_dwork);
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,6 +87,8 @@ static inline void ssusb_gadget_exit(struct ssusb_mtk *ssusb)
|
||||||
int ssusb_otg_switch_init(struct ssusb_mtk *ssusb);
|
int ssusb_otg_switch_init(struct ssusb_mtk *ssusb);
|
||||||
void ssusb_otg_switch_exit(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);
|
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
|
#else
|
||||||
|
|
||||||
|
@ -103,6 +105,10 @@ static inline int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
ssusb_set_force_mode(struct ssusb_mtk *ssusb, enum mtu3_dr_force_mode mode)
|
||||||
|
{}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* _MTU3_DR_H_ */
|
#endif /* _MTU3_DR_H_ */
|
||||||
|
|
|
@ -189,6 +189,8 @@ int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend)
|
||||||
|
|
||||||
static void ssusb_host_setup(struct ssusb_mtk *ssusb)
|
static void ssusb_host_setup(struct ssusb_mtk *ssusb)
|
||||||
{
|
{
|
||||||
|
struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
|
||||||
|
|
||||||
host_ports_num_get(ssusb);
|
host_ports_num_get(ssusb);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -197,6 +199,9 @@ static void ssusb_host_setup(struct ssusb_mtk *ssusb)
|
||||||
*/
|
*/
|
||||||
ssusb_host_enable(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 */
|
/* if port0 supports dual-role, works as host mode by default */
|
||||||
ssusb_set_vbus(&ssusb->otg_switch, 1);
|
ssusb_set_vbus(&ssusb->otg_switch, 1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -472,6 +472,8 @@
|
||||||
#define SSUSB_U3_PORT_DIS BIT(0)
|
#define SSUSB_U3_PORT_DIS BIT(0)
|
||||||
|
|
||||||
/* U3D_SSUSB_U2_CTRL_0P */
|
/* 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_VBUSVALID BIT(9)
|
||||||
#define SSUSB_U2_PORT_OTG_SEL BIT(7)
|
#define SSUSB_U2_PORT_OTG_SEL BIT(7)
|
||||||
#define SSUSB_U2_PORT_HOST BIT(2)
|
#define SSUSB_U2_PORT_HOST BIT(2)
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
#include <linux/of_irq.h>
|
#include <linux/of_irq.h>
|
||||||
#include <linux/pinctrl/consumer.h>
|
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
#include "mtu3.h"
|
#include "mtu3.h"
|
||||||
|
@ -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);
|
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 */
|
/* ignore the error if the clock does not exist */
|
||||||
static struct clk *get_optional_clk(struct device *dev, const char *id)
|
static struct clk *get_optional_clk(struct device *dev, const char *id)
|
||||||
{
|
{
|
||||||
|
@ -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");
|
dev_err(ssusb->dev, "couldn't get extcon device\n");
|
||||||
return -EPROBE_DEFER;
|
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",
|
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);
|
ssusb->dr_mode, otg_sx->is_u3_drd, ssusb->u3p_dis_msk,
|
||||||
|
otg_sx->manual_drd_enabled ? "manual" : "auto");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue