summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-class-typec8
-rw-r--r--drivers/usb/typec/class.c66
-rw-r--r--include/linux/usb/typec.h1
3 files changed, 74 insertions, 1 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-typec b/Documentation/ABI/testing/sysfs-class-typec
index b7794e02ad20..a195247d83ee 100644
--- a/Documentation/ABI/testing/sysfs-class-typec
+++ b/Documentation/ABI/testing/sysfs-class-typec
@@ -139,6 +139,14 @@ Description:
Shows if the partner supports USB Power Delivery communication:
Valid values: yes, no
+What: /sys/class/typec/<port>-partner/number_of_alternate_modes
+Date: November 2020
+Contact: Prashant Malani <pmalani@chromium.org>
+Description:
+ Shows the number of alternate modes which are advertised by the partner
+ during Power Delivery discovery. This file remains hidden until a value
+ greater than or equal to 0 is set by Type C port driver.
+
What: /sys/class/typec/<port>-partner>/identity/
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index 35eec707cb51..c7412ddbd311 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -33,6 +33,7 @@ struct typec_partner {
struct usb_pd_identity *identity;
enum typec_accessory accessory;
struct ida mode_ids;
+ int num_altmodes;
};
struct typec_port {
@@ -532,12 +533,43 @@ static ssize_t supports_usb_power_delivery_show(struct device *dev,
}
static DEVICE_ATTR_RO(supports_usb_power_delivery);
+static ssize_t number_of_alternate_modes_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct typec_partner *p = to_typec_partner(dev);
+
+ return sysfs_emit(buf, "%d\n", p->num_altmodes);
+}
+static DEVICE_ATTR_RO(number_of_alternate_modes);
+
static struct attribute *typec_partner_attrs[] = {
&dev_attr_accessory_mode.attr,
&dev_attr_supports_usb_power_delivery.attr,
+ &dev_attr_number_of_alternate_modes.attr,
+ NULL
+};
+
+static umode_t typec_partner_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n)
+{
+ struct typec_partner *partner = to_typec_partner(kobj_to_dev(kobj));
+
+ if (attr == &dev_attr_number_of_alternate_modes.attr) {
+ if (partner->num_altmodes < 0)
+ return 0;
+ }
+
+ return attr->mode;
+}
+
+static struct attribute_group typec_partner_group = {
+ .is_visible = typec_partner_attr_is_visible,
+ .attrs = typec_partner_attrs
+};
+
+static const struct attribute_group *typec_partner_groups[] = {
+ &typec_partner_group,
NULL
};
-ATTRIBUTE_GROUPS(typec_partner);
static void typec_partner_release(struct device *dev)
{
@@ -571,6 +603,37 @@ int typec_partner_set_identity(struct typec_partner *partner)
EXPORT_SYMBOL_GPL(typec_partner_set_identity);
/**
+ * typec_partner_set_num_altmodes - Set the number of available partner altmodes
+ * @partner: The partner to be updated.
+ * @num_alt_modes: The number of altmodes we want to specify as available.
+ *
+ * This routine is used to report the number of alternate modes supported by the
+ * partner. This value is *not* enforced in alternate mode registration routines.
+ *
+ * @partner.num_altmodes is set to -1 on partner registration, denoting that
+ * a valid value has not been set for it yet.
+ *
+ * Returns 0 on success or negative error number on failure.
+ */
+int typec_partner_set_num_altmodes(struct typec_partner *partner, int num_altmodes)
+{
+ int ret;
+
+ if (num_altmodes < 0)
+ return -EINVAL;
+
+ partner->num_altmodes = num_altmodes;
+ ret = sysfs_update_group(&partner->dev.kobj, &typec_partner_group);
+ if (ret < 0)
+ return ret;
+
+ sysfs_notify(&partner->dev.kobj, NULL, "number_of_alternate_modes");
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(typec_partner_set_num_altmodes);
+
+/**
* typec_partner_register_altmode - Register USB Type-C Partner Alternate Mode
* @partner: USB Type-C Partner that supports the alternate mode
* @desc: Description of the alternate mode
@@ -612,6 +675,7 @@ struct typec_partner *typec_register_partner(struct typec_port *port,
ida_init(&partner->mode_ids);
partner->usb_pd = desc->usb_pd;
partner->accessory = desc->accessory;
+ partner->num_altmodes = -1;
if (desc->identity) {
/*
diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h
index 6be558045942..bc6b1a71cb8a 100644
--- a/include/linux/usb/typec.h
+++ b/include/linux/usb/typec.h
@@ -126,6 +126,7 @@ struct typec_altmode_desc {
enum typec_port_data roles;
};
+int typec_partner_set_num_altmodes(struct typec_partner *partner, int num_altmodes);
struct typec_altmode
*typec_partner_register_altmode(struct typec_partner *partner,
const struct typec_altmode_desc *desc);