]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/commitdiff
usb: mtu3: use FORCE/RG_IDDIG to implement manual DRD switch
authorChunfeng Yun <chunfeng.yun@mediatek.com>
Fri, 13 Oct 2017 09:10:42 +0000 (17:10 +0800)
committerFelipe Balbi <felipe.balbi@linux.intel.com>
Thu, 19 Oct 2017 07:38:11 +0000 (10:38 +0300)
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>
drivers/usb/mtu3/mtu3.h
drivers/usb/mtu3/mtu3_dr.c
drivers/usb/mtu3/mtu3_dr.h
drivers/usb/mtu3/mtu3_host.c
drivers/usb/mtu3/mtu3_hw_regs.h
drivers/usb/mtu3/mtu3_plat.c

index ef2dc92a2109057b75761dac24ea747ebbd8b339..b0c2b5dca045f5277d8ff5181ce5d58c7612fdca 100644 (file)
@@ -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;
 };
 
index 560256115b23975e7d8f3c0ab0c3d6dea17df092..ec442cd5a1adcf4ecd289e4ba5bd53541536e824 100644 (file)
@@ -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);
 }
index 9b228b5811b06636232ff0393f2a90977cfa71b6..0f0cbac00192e7c4a906375ec9cd0fe25330bb72 100644 (file)
@@ -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_ */
index edcc59148171c3cdec95c4d1307da0ee4026b726..ec76b86dd887da18dec041acf525663dd240cfb0 100644 (file)
@@ -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);
 }
index b6059752dc12db51c627d2642956035ad4cdb2b5..a7e35f6ad90a5aeba1a534da40562d060ac19fb3 100644 (file)
 #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)
index fb8992011bde96ce2b48df167abc81ec29727d44..1e473b06865032a8ab3abf4f5efb941888afdb9a 100644 (file)
@@ -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;
 }