]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
usb: mtu3: register a USB Role Switch for dual role mode
authorChunfeng Yun <chunfeng.yun@mediatek.com>
Thu, 29 Aug 2019 09:22:38 +0000 (17:22 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 3 Sep 2019 18:02:15 +0000 (20:02 +0200)
Because extcon is not allowed for new bindings, and the
dual role switch is supported by USB Role Switch,
especially for Type-C drivers, so register a USB Role
Switch to support the new way

Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
Link: https://lore.kernel.org/r/1567070558-29417-12-git-send-email-chunfeng.yun@mediatek.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/mtu3/Kconfig
drivers/usb/mtu3/mtu3.h
drivers/usb/mtu3/mtu3_debugfs.c
drivers/usb/mtu3/mtu3_dr.c
drivers/usb/mtu3/mtu3_dr.h
drivers/usb/mtu3/mtu3_plat.c

index 928c2cd6fc0084ef0feb7f79edf6d577e8fa5a46..bf98fd36341dab732590882e47e4e09b4eb41317 100644 (file)
@@ -44,6 +44,7 @@ config USB_MTU3_DUAL_ROLE
        bool "Dual Role mode"
        depends on ((USB=y || USB=USB_MTU3) && (USB_GADGET=y || USB_GADGET=USB_MTU3))
        depends on (EXTCON=y || EXTCON=USB_MTU3)
+       select USB_ROLE_SWITCH
        help
          This is the default mode of working of MTU3 controller where
          both host and gadget features are enabled.
index 76ecf12fdf62c857b43ac129041990551dd3369a..6087be236a350410caf4e9d7e44b7de91b23e966 100644 (file)
@@ -199,6 +199,9 @@ struct mtu3_gpd_ring {
 * @id_nb : notifier for iddig(idpin) detection
 * @id_work : work of iddig detection notifier
 * @id_event : event of iddig detecion notifier
+* @role_sw : use USB Role Switch to support dual-role switch, can't use
+*              extcon at the same time, and extcon is deprecated.
+* @role_sw_used : true when the USB Role Switch is used.
 * @is_u3_drd: whether port0 supports usb3.0 dual-role device or not
 * @manual_drd_enabled: it's true when supports dual-role device by debugfs
 *              to switch host/device modes depending on user input.
@@ -212,6 +215,8 @@ struct otg_switch_mtk {
        struct notifier_block id_nb;
        struct work_struct id_work;
        unsigned long id_event;
+       struct usb_role_switch *role_sw;
+       bool role_sw_used;
        bool is_u3_drd;
        bool manual_drd_enabled;
 };
index 62c57ddc554e4cf26bad6f680784ade644e82a94..c96e5dab0a480fea8a8fb8e6ad17c69e50b71a6d 100644 (file)
@@ -453,9 +453,9 @@ static ssize_t ssusb_mode_write(struct file *file, const char __user *ubuf,
                return -EFAULT;
 
        if (!strncmp(buf, "host", 4) && !ssusb->is_host) {
-               ssusb_mode_manual_switch(ssusb, 1);
+               ssusb_mode_switch(ssusb, 1);
        } else if (!strncmp(buf, "device", 6) && ssusb->is_host) {
-               ssusb_mode_manual_switch(ssusb, 0);
+               ssusb_mode_switch(ssusb, 0);
        } else {
                dev_err(ssusb->dev, "wrong or duplicated setting\n");
                return -EINVAL;
index 5fcb71af875a4fcf96650044d3673503355fc8b9..08e18448e8b82777f361d9080085f4abc129cc6b 100644 (file)
@@ -7,6 +7,8 @@
  * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
  */
 
+#include <linux/usb/role.h>
+
 #include "mtu3.h"
 #include "mtu3_dr.h"
 #include "mtu3_debug.h"
@@ -280,7 +282,7 @@ static int ssusb_extcon_register(struct otg_switch_mtk *otg_sx)
  * This is useful in special cases, such as uses TYPE-A receptacle but also
  * wants to support dual-role mode.
  */
-void ssusb_mode_manual_switch(struct ssusb_mtk *ssusb, int to_host)
+void ssusb_mode_switch(struct ssusb_mtk *ssusb, int to_host)
 {
        struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
 
@@ -318,6 +320,47 @@ void ssusb_set_force_mode(struct ssusb_mtk *ssusb,
        mtu3_writel(ssusb->ippc_base, SSUSB_U2_CTRL(0), value);
 }
 
+static int ssusb_role_sw_set(struct device *dev, enum usb_role role)
+{
+       struct ssusb_mtk *ssusb = dev_get_drvdata(dev);
+       bool to_host = false;
+
+       if (role == USB_ROLE_HOST)
+               to_host = true;
+
+       if (to_host ^ ssusb->is_host)
+               ssusb_mode_switch(ssusb, to_host);
+
+       return 0;
+}
+
+static enum usb_role ssusb_role_sw_get(struct device *dev)
+{
+       struct ssusb_mtk *ssusb = dev_get_drvdata(dev);
+       enum usb_role role;
+
+       role = ssusb->is_host ? USB_ROLE_HOST : USB_ROLE_DEVICE;
+
+       return role;
+}
+
+static int ssusb_role_sw_register(struct otg_switch_mtk *otg_sx)
+{
+       struct usb_role_switch_desc role_sx_desc = { 0 };
+       struct ssusb_mtk *ssusb =
+               container_of(otg_sx, struct ssusb_mtk, otg_switch);
+
+       if (!otg_sx->role_sw_used)
+               return 0;
+
+       role_sx_desc.set = ssusb_role_sw_set;
+       role_sx_desc.get = ssusb_role_sw_get;
+       role_sx_desc.fwnode = dev_fwnode(ssusb->dev);
+       otg_sx->role_sw = usb_role_switch_register(ssusb->dev, &role_sx_desc);
+
+       return PTR_ERR_OR_ZERO(otg_sx->role_sw);
+}
+
 int ssusb_otg_switch_init(struct ssusb_mtk *ssusb)
 {
        struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
@@ -328,6 +371,8 @@ int ssusb_otg_switch_init(struct ssusb_mtk *ssusb)
 
        if (otg_sx->manual_drd_enabled)
                ssusb_dr_debugfs_init(ssusb);
+       else if (otg_sx->role_sw_used)
+               ret = ssusb_role_sw_register(otg_sx);
        else
                ret = ssusb_extcon_register(otg_sx);
 
@@ -340,4 +385,5 @@ void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb)
 
        cancel_work_sync(&otg_sx->id_work);
        cancel_work_sync(&otg_sx->vbus_work);
+       usb_role_switch_unregister(otg_sx->role_sw);
 }
index ba6fe357ce29bfffd4dd690e5de433af0ff8ae5c..5e58c4dbd54af19854ee75d810edcc9c77a3e4b0 100644 (file)
@@ -71,7 +71,7 @@ static inline void ssusb_gadget_exit(struct ssusb_mtk *ssusb)
 #if IS_ENABLED(CONFIG_USB_MTU3_DUAL_ROLE)
 int ssusb_otg_switch_init(struct ssusb_mtk *ssusb);
 void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb);
-void ssusb_mode_manual_switch(struct ssusb_mtk *ssusb, int to_host);
+void ssusb_mode_switch(struct ssusb_mtk *ssusb, int to_host);
 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);
@@ -86,8 +86,8 @@ static inline int ssusb_otg_switch_init(struct ssusb_mtk *ssusb)
 static inline void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb)
 {}
 
-static inline void
-ssusb_mode_manual_switch(struct ssusb_mtk *ssusb, int to_host) {}
+static inline void ssusb_mode_switch(struct ssusb_mtk *ssusb, int to_host)
+{}
 
 static inline int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on)
 {
index fd0f6c5dfbc179c54a68b0e9a2608102af5ffabb..9c256ea3cdf5c10a7f433d34aae4766a43cf175e 100644 (file)
@@ -299,8 +299,9 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb)
        otg_sx->is_u3_drd = of_property_read_bool(node, "mediatek,usb3-drd");
        otg_sx->manual_drd_enabled =
                of_property_read_bool(node, "enable-manual-drd");
+       otg_sx->role_sw_used = of_property_read_bool(node, "usb-role-switch");
 
-       if (of_property_read_bool(node, "extcon")) {
+       if (!otg_sx->role_sw_used && of_property_read_bool(node, "extcon")) {
                otg_sx->edev = extcon_get_edev_by_phandle(ssusb->dev, 0);
                if (IS_ERR(otg_sx->edev)) {
                        dev_err(ssusb->dev, "couldn't get extcon device\n");