extcon: ptn5150: Add usb role class support
authorLi Jun <jun.li@nxp.com>
Tue, 19 Apr 2022 12:44:09 +0000 (20:44 +0800)
committerChanwoo Choi <cw00.choi@samsung.com>
Fri, 13 May 2022 08:03:41 +0000 (17:03 +0900)
Some usb controller drivers may not support extcon but use
usb role class as it's the preferred approach, so to support
usb dual role switch with usb role class, add usb role class
consumer support.

Signed-off-by: Li Jun <jun.li@nxp.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
drivers/extcon/Kconfig
drivers/extcon/extcon-ptn5150.c

index 88a8b3d..dca7cec 100644 (file)
@@ -131,6 +131,7 @@ config EXTCON_PALMAS
 config EXTCON_PTN5150
        tristate "NXP PTN5150 CC LOGIC USB EXTCON support"
        depends on I2C && (GPIOLIB || COMPILE_TEST)
+       depends on USB_ROLE_SWITCH || !USB_ROLE_SWITCH
        select REGMAP_I2C
        help
          Say Y here to enable support for USB peripheral and USB host
index 2a78741..017a071 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/slab.h>
 #include <linux/extcon-provider.h>
 #include <linux/gpio/consumer.h>
+#include <linux/usb/role.h>
 
 /* PTN5150 registers */
 #define PTN5150_REG_DEVICE_ID                  0x01
@@ -52,6 +53,7 @@ struct ptn5150_info {
        int irq;
        struct work_struct irq_work;
        struct mutex mutex;
+       struct usb_role_switch *role_sw;
 };
 
 /* List of detectable cables */
@@ -70,6 +72,7 @@ static const struct regmap_config ptn5150_regmap_config = {
 static void ptn5150_check_state(struct ptn5150_info *info)
 {
        unsigned int port_status, reg_data, vbus;
+       enum usb_role usb_role = USB_ROLE_NONE;
        int ret;
 
        ret = regmap_read(info->regmap, PTN5150_REG_CC_STATUS, &reg_data);
@@ -85,6 +88,7 @@ static void ptn5150_check_state(struct ptn5150_info *info)
                extcon_set_state_sync(info->edev, EXTCON_USB_HOST, false);
                gpiod_set_value_cansleep(info->vbus_gpiod, 0);
                extcon_set_state_sync(info->edev, EXTCON_USB, true);
+               usb_role = USB_ROLE_DEVICE;
                break;
        case PTN5150_UFP_ATTACHED:
                extcon_set_state_sync(info->edev, EXTCON_USB, false);
@@ -95,10 +99,18 @@ static void ptn5150_check_state(struct ptn5150_info *info)
                        gpiod_set_value_cansleep(info->vbus_gpiod, 1);
 
                extcon_set_state_sync(info->edev, EXTCON_USB_HOST, true);
+               usb_role = USB_ROLE_HOST;
                break;
        default:
                break;
        }
+
+       if (usb_role) {
+               ret = usb_role_switch_set_role(info->role_sw, usb_role);
+               if (ret)
+                       dev_err(info->dev, "failed to set %s role: %d\n",
+                               usb_role_string(usb_role), ret);
+       }
 }
 
 static void ptn5150_irq_work(struct work_struct *work)
@@ -133,6 +145,13 @@ static void ptn5150_irq_work(struct work_struct *work)
                        extcon_set_state_sync(info->edev,
                                        EXTCON_USB, false);
                        gpiod_set_value_cansleep(info->vbus_gpiod, 0);
+
+                       ret = usb_role_switch_set_role(info->role_sw,
+                                                      USB_ROLE_NONE);
+                       if (ret)
+                               dev_err(info->dev,
+                                       "failed to set none role: %d\n",
+                                       ret);
                }
        }
 
@@ -199,6 +218,7 @@ static void ptn5150_work_sync_and_put(void *data)
        struct ptn5150_info *info = data;
 
        cancel_work_sync(&info->irq_work);
+       usb_role_switch_put(info->role_sw);
 }
 
 static int ptn5150_i2c_probe(struct i2c_client *i2c)
@@ -291,6 +311,11 @@ static int ptn5150_i2c_probe(struct i2c_client *i2c)
        if (ret)
                return -EINVAL;
 
+       info->role_sw = usb_role_switch_get(info->dev);
+       if (IS_ERR(info->role_sw))
+               return dev_err_probe(info->dev, PTR_ERR(info->role_sw),
+                                    "failed to get role switch\n");
+
        ret = devm_add_action_or_reset(dev, ptn5150_work_sync_and_put, info);
        if (ret)
                return ret;