diff options
author | Chunfeng Yun <chunfeng.yun@mediatek.com> | 2017-10-13 17:10:42 +0800 |
---|---|---|
committer | Felipe Balbi <felipe.balbi@linux.intel.com> | 2017-10-19 10:38:11 +0300 |
commit | c776f2c3e81308977e95a228b0665e3d5c63dff3 (patch) | |
tree | 2e0daefc114ea8a8e6ab388b72264749d4157d81 /drivers | |
parent | 1a46dfea0841d5ebc08fafe7b3f60d33581f8e27 (diff) | |
download | linux-c776f2c3e81308977e95a228b0665e3d5c63dff3.tar.bz2 |
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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/mtu3/mtu3.h | 18 | ||||
-rw-r--r-- | drivers/usb/mtu3/mtu3_dr.c | 61 | ||||
-rw-r--r-- | drivers/usb/mtu3/mtu3_dr.h | 6 | ||||
-rw-r--r-- | drivers/usb/mtu3/mtu3_host.c | 5 | ||||
-rw-r--r-- | drivers/usb/mtu3/mtu3_hw_regs.h | 2 | ||||
-rw-r--r-- | drivers/usb/mtu3/mtu3_plat.c | 38 |
6 files changed, 74 insertions, 56 deletions
diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h index ef2dc92a2109..b0c2b5dca045 100644 --- a/drivers/usb/mtu3/mtu3.h +++ b/drivers/usb/mtu3/mtu3.h @@ -115,6 +115,19 @@ enum mtu3_g_ep0_state { }; /** + * 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 * @bitmap: fifo bitmap in unit of @MTU3_EP_FIFO_UNIT @@ -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; }; diff --git a/drivers/usb/mtu3/mtu3_dr.c b/drivers/usb/mtu3/mtu3_dr.c index 560256115b23..ec442cd5a1ad 100644 --- a/drivers/usb/mtu3/mtu3_dr.c +++ b/drivers/usb/mtu3/mtu3_dr.c @@ -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; @@ -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; } @@ -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); } diff --git a/drivers/usb/mtu3/mtu3_dr.h b/drivers/usb/mtu3/mtu3_dr.h index 9b228b5811b0..0f0cbac00192 100644 --- a/drivers/usb/mtu3/mtu3_dr.h +++ b/drivers/usb/mtu3/mtu3_dr.h @@ -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_ */ diff --git a/drivers/usb/mtu3/mtu3_host.c b/drivers/usb/mtu3/mtu3_host.c index edcc59148171..ec76b86dd887 100644 --- a/drivers/usb/mtu3/mtu3_host.c +++ b/drivers/usb/mtu3/mtu3_host.c @@ -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); } diff --git a/drivers/usb/mtu3/mtu3_hw_regs.h b/drivers/usb/mtu3/mtu3_hw_regs.h index b6059752dc12..a7e35f6ad90a 100644 --- a/drivers/usb/mtu3/mtu3_hw_regs.h +++ b/drivers/usb/mtu3/mtu3_hw_regs.h @@ -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) diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c index fb8992011bde..1e473b068650 100644 --- a/drivers/usb/mtu3/mtu3_plat.c +++ b/drivers/usb/mtu3/mtu3_plat.c @@ -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" @@ -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) { @@ -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; } |