Add controls for whether an alternate mode is automatically entered when
a partner connects. The auto_enter control is only available on ports
and applies immediately after a partner connects. The default behavior
is to enable auto enter and drivers must explicitly disable it.
Signed-off-by: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
---
Documentation/ABI/testing/sysfs-bus-typec | 9 +++++++
drivers/usb/typec/altmodes/displayport.c | 6 +++--
drivers/usb/typec/altmodes/thunderbolt.c | 3 ++-
drivers/usb/typec/class.c | 31 +++++++++++++++++++++++
include/linux/usb/typec.h | 2 ++
include/linux/usb/typec_altmode.h | 2 ++
6 files changed, 50 insertions(+), 3 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-typec b/Documentation/ABI/testing/sysfs-bus-typec
index 205d9c91e2e1..f09d05727b82 100644
--- a/Documentation/ABI/testing/sysfs-bus-typec
+++ b/Documentation/ABI/testing/sysfs-bus-typec
@@ -12,6 +12,15 @@ Description:
Valid values are boolean.
+What: /sys/bus/typec/devices/.../auto_enter
+Date: September 2024
+Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+ Controls whether a mode will be automatically entered when a partner is
+ connected.
+
+ This field is only valid and displayed on a port. Valid values are boolean.
+
What: /sys/bus/typec/devices/.../description
Date: July 2018
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c
index 92cc1b136120..7b164086bbbb 100644
--- a/drivers/usb/typec/altmodes/displayport.c
+++ b/drivers/usb/typec/altmodes/displayport.c
@@ -767,8 +767,10 @@ int dp_altmode_probe(struct typec_altmode *alt)
if (plug)
typec_altmode_set_drvdata(plug, dp);
- dp->state = plug ? DP_STATE_ENTER_PRIME : DP_STATE_ENTER;
- schedule_work(&dp->work);
+ if (port->auto_enter) {
+ dp->state = plug ? DP_STATE_ENTER_PRIME : DP_STATE_ENTER;
+ schedule_work(&dp->work);
+ }
return 0;
}
diff --git a/drivers/usb/typec/altmodes/thunderbolt.c b/drivers/usb/typec/altmodes/thunderbolt.c
index 515e775ee41a..ba2ddaf3245e 100644
--- a/drivers/usb/typec/altmodes/thunderbolt.c
+++ b/drivers/usb/typec/altmodes/thunderbolt.c
@@ -225,6 +225,7 @@ static const struct typec_altmode_ops tbt_altmode_ops = {
static int tbt_altmode_probe(struct typec_altmode *alt)
{
+ const struct typec_altmode *port = typec_altmode_get_partner(alt);
struct tbt_altmode *tbt;
tbt = devm_kzalloc(&alt->dev, sizeof(*tbt), GFP_KERNEL);
@@ -239,7 +240,7 @@ static int tbt_altmode_probe(struct typec_altmode *alt)
typec_altmode_set_drvdata(alt, tbt);
typec_altmode_set_ops(alt, &tbt_altmode_ops);
- if (tbt_ready(alt)) {
+ if (port->auto_enter && tbt_ready(alt)) {
if (tbt->plug[TYPEC_PLUG_SOP_PP])
tbt->state = TBT_STATE_SOP_PP_ENTER;
else if (tbt->plug[TYPEC_PLUG_SOP_P])
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index 179856503d5d..a7ae0cdecca0 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -397,6 +397,31 @@ static ssize_t active_store(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR_RW(active);
+static ssize_t
+auto_enter_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct typec_altmode *alt = to_typec_altmode(dev);
+
+ return sprintf(buf, "%s\n", alt->auto_enter ? "yes" : "no");
+}
+
+static ssize_t auto_enter_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct typec_altmode *adev = to_typec_altmode(dev);
+ bool auto_enter;
+ int ret;
+
+ ret = kstrtobool(buf, &auto_enter);
+ if (ret)
+ return ret;
+
+ adev->auto_enter = auto_enter;
+
+ return size;
+}
+static DEVICE_ATTR_RW(auto_enter);
+
static ssize_t
supported_roles_show(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -440,6 +465,7 @@ static DEVICE_ATTR_RO(svid);
static struct attribute *typec_altmode_attrs[] = {
&dev_attr_active.attr,
+ &dev_attr_auto_enter.attr,
&dev_attr_mode.attr,
&dev_attr_svid.attr,
&dev_attr_vdo.attr,
@@ -455,6 +481,10 @@ static umode_t typec_altmode_attr_is_visible(struct kobject *kobj,
if (!adev->ops || !adev->ops->activate)
return 0444;
+ if (attr == &dev_attr_auto_enter.attr)
+ if (!is_typec_port(adev->dev.parent))
+ return 0;
+
return attr->mode;
}
@@ -557,6 +587,7 @@ typec_register_altmode(struct device *parent,
if (is_port) {
alt->attrs[3] = &dev_attr_supported_roles.attr;
alt->adev.active = true; /* Enabled by default */
+ alt->adev.auto_enter = !desc->no_auto_enter;
}
sprintf(alt->group_name, "mode%d", desc->mode);
diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h
index 549275f8ac1b..67242355f78e 100644
--- a/include/linux/usb/typec.h
+++ b/include/linux/usb/typec.h
@@ -128,6 +128,7 @@ int typec_cable_set_identity(struct typec_cable *cable);
* @svid: Standard or Vendor ID
* @mode: Index of the Mode
* @vdo: VDO returned by Discover Modes USB PD command
+ * @no_auto_enter: Only for ports. Disables auto enter which is default behavior.
* @roles: Only for ports. DRP if the mode is available in both roles
*
* Description of an Alternate Mode which a connector, cable plug or partner
@@ -137,6 +138,7 @@ struct typec_altmode_desc {
u16 svid;
u8 mode;
u32 vdo;
+ bool no_auto_enter;
/* Only used with ports */
enum typec_port_data roles;
};
diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_altmode.h
index b3c0866ea70f..ab7c3ebe4926 100644
--- a/include/linux/usb/typec_altmode.h
+++ b/include/linux/usb/typec_altmode.h
@@ -18,6 +18,7 @@ struct typec_altmode_ops;
* @mode: Index of the Mode
* @vdo: VDO returned by Discover Modes USB PD command
* @active: Tells has the mode been entered or not
+ * @auto_enter: Tells whether to auto-enter mode (only valid for port mode).
* @desc: Optional human readable description of the mode
* @ops: Operations vector from the driver
* @cable_ops: Cable operations vector from the driver.
@@ -28,6 +29,7 @@ struct typec_altmode {
int mode;
u32 vdo;
unsigned int active:1;
+ unsigned int auto_enter:1;
char *desc;
const struct typec_altmode_ops *ops;
--
2.46.0.792.g87dc391469-goog