diff mbox series

[v2,2/3] usb: typec: USB Power Deliver helpers for ports and partners

Message ID 20220412130023.83927-3-heikki.krogerus@linux.intel.com
State New
Headers show
Series usb: typec: Separate sysfs directory for all USB PD objects | expand

Commit Message

Heikki Krogerus April 12, 2022, 1 p.m. UTC
All the USB Type-C Connector Class devices are protected, so
the drivers can not directly access them. This will adds a
few helpers that can be used to link the ports and partners
to the correct USB Power Delivery objects.

For ports a new optional sysfs attribute file is also added
that can be used to select the USB Power Delivery
capabilities that the port will advertise to the partner.

Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
---
 Documentation/ABI/testing/sysfs-class-typec |   8 +
 drivers/usb/typec/class.c                   | 153 ++++++++++++++++++++
 drivers/usb/typec/class.h                   |   4 +
 include/linux/usb/typec.h                   |  12 ++
 4 files changed, 177 insertions(+)

Comments

kernel test robot April 12, 2022, 3:35 p.m. UTC | #1
Hi Heikki,

I love your patch! Perhaps something to improve:

[auto build test WARNING on usb/usb-testing]
[also build test WARNING on chrome-platform/for-next v5.18-rc2 next-20220412]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/intel-lab-lkp/linux/commits/Heikki-Krogerus/usb-typec-Separate-sysfs-directory-for-all-USB-PD-objects/20220412-211628
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
config: alpha-allyesconfig (https://download.01.org/0day-ci/archive/20220412/202204122323.iqiRhgAI-lkp@intel.com/config)
compiler: alpha-linux-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/98fece77872792e49f1005617761a533089f319d
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Heikki-Krogerus/usb-typec-Separate-sysfs-directory-for-all-USB-PD-objects/20220412-211628
        git checkout 98fece77872792e49f1005617761a533089f319d
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=alpha SHELL=/bin/bash drivers/usb/typec/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> drivers/usb/typec/pd.c:669: warning: expecting prototype for pd_register(). Prototype was for pd_unregister() instead
>> drivers/usb/typec/pd.c:717: warning: expecting prototype for pd_link_device(). Prototype was for pd_unlink_device() instead
--
>> drivers/usb/typec/class.c:1222: warning: expecting prototype for typec_port_set_pd_capabilities(). Prototype was for typec_port_set_pd() instead


vim +717 drivers/usb/typec/pd.c

c99df888db9e4c Heikki Krogerus 2022-04-12  663  
c99df888db9e4c Heikki Krogerus 2022-04-12  664  /**
c99df888db9e4c Heikki Krogerus 2022-04-12  665   * pd_register - Unregister USB Power Delivery Support.
c99df888db9e4c Heikki Krogerus 2022-04-12  666   * @pd: The USB PD contract.
c99df888db9e4c Heikki Krogerus 2022-04-12  667   */
c99df888db9e4c Heikki Krogerus 2022-04-12  668  void pd_unregister(struct pd *pd)
c99df888db9e4c Heikki Krogerus 2022-04-12 @669  {
c99df888db9e4c Heikki Krogerus 2022-04-12  670  	if (IS_ERR_OR_NULL(pd))
c99df888db9e4c Heikki Krogerus 2022-04-12  671  		return;
c99df888db9e4c Heikki Krogerus 2022-04-12  672  
c99df888db9e4c Heikki Krogerus 2022-04-12  673  	kobject_put(&pd->kobj);
c99df888db9e4c Heikki Krogerus 2022-04-12  674  }
c99df888db9e4c Heikki Krogerus 2022-04-12  675  EXPORT_SYMBOL_GPL(pd_unregister);
c99df888db9e4c Heikki Krogerus 2022-04-12  676  
c99df888db9e4c Heikki Krogerus 2022-04-12  677  /**
c99df888db9e4c Heikki Krogerus 2022-04-12  678   * pd_link_device - Link device to its USB PD object.
c99df888db9e4c Heikki Krogerus 2022-04-12  679   * @pd: The USB PD instance.
c99df888db9e4c Heikki Krogerus 2022-04-12  680   * @dev: The device.
c99df888db9e4c Heikki Krogerus 2022-04-12  681   *
c99df888db9e4c Heikki Krogerus 2022-04-12  682   * This function can be used to create a symlink named "usb_power_delivery" for
c99df888db9e4c Heikki Krogerus 2022-04-12  683   * @dev that points to @pd.
c99df888db9e4c Heikki Krogerus 2022-04-12  684   */
c99df888db9e4c Heikki Krogerus 2022-04-12  685  int pd_link_device(struct pd *pd, struct device *dev)
c99df888db9e4c Heikki Krogerus 2022-04-12  686  {
c99df888db9e4c Heikki Krogerus 2022-04-12  687  	int ret;
c99df888db9e4c Heikki Krogerus 2022-04-12  688  
c99df888db9e4c Heikki Krogerus 2022-04-12  689  	if (IS_ERR_OR_NULL(pd) || !dev)
c99df888db9e4c Heikki Krogerus 2022-04-12  690  		return 0;
c99df888db9e4c Heikki Krogerus 2022-04-12  691  
c99df888db9e4c Heikki Krogerus 2022-04-12  692  	ret = sysfs_create_link(&dev->kobj, &pd->kobj, "usb_power_delivery");
c99df888db9e4c Heikki Krogerus 2022-04-12  693  	if (ret)
c99df888db9e4c Heikki Krogerus 2022-04-12  694  		return ret;
c99df888db9e4c Heikki Krogerus 2022-04-12  695  
c99df888db9e4c Heikki Krogerus 2022-04-12  696  	ret = sysfs_create_link(&pd->kobj, &dev->kobj, dev_name(dev));
c99df888db9e4c Heikki Krogerus 2022-04-12  697  	if (ret) {
c99df888db9e4c Heikki Krogerus 2022-04-12  698  		sysfs_remove_link(&dev->kobj, "usb_power_delivery");
c99df888db9e4c Heikki Krogerus 2022-04-12  699  		return ret;
c99df888db9e4c Heikki Krogerus 2022-04-12  700  	}
c99df888db9e4c Heikki Krogerus 2022-04-12  701  
c99df888db9e4c Heikki Krogerus 2022-04-12  702  	kobject_get(&pd->kobj);
c99df888db9e4c Heikki Krogerus 2022-04-12  703  	get_device(dev);
c99df888db9e4c Heikki Krogerus 2022-04-12  704  
c99df888db9e4c Heikki Krogerus 2022-04-12  705  	return 0;
c99df888db9e4c Heikki Krogerus 2022-04-12  706  }
c99df888db9e4c Heikki Krogerus 2022-04-12  707  EXPORT_SYMBOL_GPL(pd_link_device);
c99df888db9e4c Heikki Krogerus 2022-04-12  708  
c99df888db9e4c Heikki Krogerus 2022-04-12  709  /**
c99df888db9e4c Heikki Krogerus 2022-04-12  710   * pd_link_device - Unlink device from its USB PD object.
c99df888db9e4c Heikki Krogerus 2022-04-12  711   * @pd: The USB PD instance.
c99df888db9e4c Heikki Krogerus 2022-04-12  712   * @dev: The device.
c99df888db9e4c Heikki Krogerus 2022-04-12  713   *
c99df888db9e4c Heikki Krogerus 2022-04-12  714   * Remove the symlink that was previously created with pd_link_device().
c99df888db9e4c Heikki Krogerus 2022-04-12  715   */
c99df888db9e4c Heikki Krogerus 2022-04-12  716  void pd_unlink_device(struct pd *pd, struct device *dev)
c99df888db9e4c Heikki Krogerus 2022-04-12 @717  {
c99df888db9e4c Heikki Krogerus 2022-04-12  718  	if (IS_ERR_OR_NULL(pd) || !dev)
c99df888db9e4c Heikki Krogerus 2022-04-12  719  		return;
c99df888db9e4c Heikki Krogerus 2022-04-12  720  
c99df888db9e4c Heikki Krogerus 2022-04-12  721  	sysfs_remove_link(&dev->kobj, "usb_power_delivery");
c99df888db9e4c Heikki Krogerus 2022-04-12  722  	sysfs_remove_link(&pd->kobj, dev_name(dev));
c99df888db9e4c Heikki Krogerus 2022-04-12  723  	kobject_put(&pd->kobj);
c99df888db9e4c Heikki Krogerus 2022-04-12  724  	put_device(dev);
c99df888db9e4c Heikki Krogerus 2022-04-12  725  }
c99df888db9e4c Heikki Krogerus 2022-04-12  726  EXPORT_SYMBOL_GPL(pd_unlink_device);
c99df888db9e4c Heikki Krogerus 2022-04-12  727
kernel test robot April 12, 2022, 4:16 p.m. UTC | #2
Hi Heikki,

I love your patch! Perhaps something to improve:

[auto build test WARNING on usb/usb-testing]
[also build test WARNING on chrome-platform/for-next v5.18-rc2 next-20220412]
[cannot apply to balbi-usb/testing/next peter-chen-usb/for-usb-next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/intel-lab-lkp/linux/commits/Heikki-Krogerus/usb-typec-Separate-sysfs-directory-for-all-USB-PD-objects/20220412-211628
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
config: hexagon-buildonly-randconfig-r005-20220412 (https://download.01.org/0day-ci/archive/20220413/202204130028.O6wRGxzY-lkp@intel.com/config)
compiler: clang version 15.0.0 (https://github.com/llvm/llvm-project fe2478d44e4f7f191c43fef629ac7a23d0251e72)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/98fece77872792e49f1005617761a533089f319d
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Heikki-Krogerus/usb-typec-Separate-sysfs-directory-for-all-USB-PD-objects/20220412-211628
        git checkout 98fece77872792e49f1005617761a533089f319d
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=hexagon SHELL=/bin/bash drivers/usb/typec/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> drivers/usb/typec/class.c:30:27: warning: unused variable 'cap_name' [-Wunused-const-variable]
   static const char * const cap_name[] = {
                             ^
   1 warning generated.


vim +/cap_name +30 drivers/usb/typec/class.c

    29	
  > 30	static const char * const cap_name[] = {
    31		[TYPEC_SINK]		= "sink-capabilities",
    32		[TYPEC_SOURCE]		= "source-capabilities",
    33	};
    34
diff mbox series

Patch

diff --git a/Documentation/ABI/testing/sysfs-class-typec b/Documentation/ABI/testing/sysfs-class-typec
index 75088ecad2029..6c528f87ecaf0 100644
--- a/Documentation/ABI/testing/sysfs-class-typec
+++ b/Documentation/ABI/testing/sysfs-class-typec
@@ -141,6 +141,14 @@  Description:
 		- "reverse": CC2 orientation
 		- "unknown": Orientation cannot be determined.
 
+What:		/sys/class/typec/<port>/select_usb_power_delivery
+Date:		February 2022
+Contact:	Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+		Lists the USB Power Delivery Capabilities that the port can
+		advertise to the partner. The currently used capabilities are in
+		brackets. Selection happens by writing to the file.
+
 USB Type-C partner devices (eg. /sys/class/typec/port0-partner/)
 
 What:		/sys/class/typec/<port>-partner/accessory_mode
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index ee0e520707dd7..770d7d38c9b57 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -15,6 +15,7 @@ 
 
 #include "bus.h"
 #include "class.h"
+#include "pd.h"
 
 static DEFINE_IDA(typec_index_ida);
 
@@ -26,6 +27,11 @@  struct class typec_class = {
 /* ------------------------------------------------------------------------- */
 /* Common attributes */
 
+static const char * const cap_name[] = {
+	[TYPEC_SINK]		= "sink-capabilities",
+	[TYPEC_SOURCE]		= "source-capabilities",
+};
+
 static const char * const typec_accessory_modes[] = {
 	[TYPEC_ACCESSORY_NONE]		= "none",
 	[TYPEC_ACCESSORY_AUDIO]		= "analog_audio",
@@ -720,6 +726,38 @@  void typec_partner_set_pd_revision(struct typec_partner *partner, u16 pd_revisio
 }
 EXPORT_SYMBOL_GPL(typec_partner_set_pd_revision);
 
+/**
+ * typec_partner_set_pd - Declare USB Power Delivery Contract.
+ * @partner: The partner device.
+ * @pd: The USB PD instance.
+ *
+ * This routine can be used to declare USB Power Delivery Contract with @partner
+ * by linking @partner to @pd which contains the objects that were used during the
+ * negotiation of the contract.
+ *
+ * If @pd is NULL, the link is removed and the contract with @partner has ended.
+ */
+int typec_partner_set_pd(struct typec_partner *partner, struct pd *pd)
+{
+	int ret;
+
+	if (IS_ERR_OR_NULL(partner) || partner->pd == pd)
+		return 0;
+
+	if (pd) {
+		ret = pd_link_device(pd, &partner->dev);
+		if (ret)
+			return ret;
+	} else {
+		pd_unlink_device(partner->pd, &partner->dev);
+	}
+
+	partner->pd = pd;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(typec_partner_set_pd);
+
 /**
  * typec_partner_set_num_altmodes - Set the number of available partner altmodes
  * @partner: The partner to be updated.
@@ -1170,6 +1208,104 @@  EXPORT_SYMBOL_GPL(typec_unregister_cable);
 /* ------------------------------------------------------------------------- */
 /* USB Type-C ports */
 
+/**
+ * typec_port_set_pd_capabilities - Assign USB PD for port.
+ * @port: USB Type-C port.
+ * @pd: USB PD instance.
+ *
+ * This routine can be used to set the USB Power Delivery Capabilities for @port
+ * that it will advertise to the partner.
+ *
+ * If @pd is NULL, the assignment is removed.
+ */
+int typec_port_set_pd(struct typec_port *port, struct pd *pd)
+{
+	int ret;
+
+	if (IS_ERR_OR_NULL(port) || port->pd == pd)
+		return 0;
+
+	if (pd) {
+		ret = pd_link_device(pd, &port->dev);
+		if (ret)
+			return ret;
+	} else {
+		pd_unlink_device(port->pd, &port->dev);
+	}
+
+	port->pd = pd;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(typec_port_set_pd);
+
+static ssize_t select_usb_power_delivery_store(struct device *dev,
+					       struct device_attribute *attr,
+					       const char *buf, size_t size)
+{
+	struct typec_port *port = to_typec_port(dev);
+	struct pd *pd;
+
+	if (!port->ops || !port->ops->pd_set)
+		return -EOPNOTSUPP;
+
+	pd = pd_find(buf);
+	if (!pd)
+		return -EINVAL;
+
+	return port->ops->pd_set(port, pd);
+}
+
+static ssize_t select_usb_power_delivery_show(struct device *dev,
+					      struct device_attribute *attr, char *buf)
+{
+	struct typec_port *port = to_typec_port(dev);
+	struct pd **pds;
+	struct pd *pd;
+	int ret = 0;
+
+	if (!port->ops || !port->ops->pd_get)
+		return -EOPNOTSUPP;
+
+	pds = port->ops->pd_get(port);
+	if (!pds)
+		return 0;
+
+	for (pd = pds[0]; pd; pd++) {
+		if (pd == port->pd)
+			ret += sysfs_emit(buf + ret, "[%s] ", kobject_name(&pd->kobj));
+		else
+			ret += sysfs_emit(buf + ret, "%s ", kobject_name(&pd->kobj));
+	}
+
+	buf[ret - 1] = '\n';
+
+	return ret;
+}
+static DEVICE_ATTR_RW(select_usb_power_delivery);
+
+static struct attribute *port_attrs[] = {
+	&dev_attr_select_usb_power_delivery.attr,
+	NULL
+};
+
+static umode_t port_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n)
+{
+	struct typec_port *port = to_typec_port(kobj_to_dev(kobj));
+
+	if (!port->pd || !port->ops || !port->ops->pd_get)
+		return 0;
+	if (!port->ops->pd_set)
+		return 0444;
+
+	return attr->mode;
+}
+
+static const struct attribute_group pd_group = {
+	.is_visible = port_attr_is_visible,
+	.attrs = port_attrs,
+};
+
 static const char * const typec_orientations[] = {
 	[TYPEC_ORIENTATION_NONE]	= "unknown",
 	[TYPEC_ORIENTATION_NORMAL]	= "normal",
@@ -1581,6 +1717,7 @@  static const struct attribute_group typec_group = {
 
 static const struct attribute_group *typec_groups[] = {
 	&typec_group,
+	&pd_group,
 	NULL
 };
 
@@ -2123,6 +2260,13 @@  struct typec_port *typec_register_port(struct device *parent,
 		return ERR_PTR(ret);
 	}
 
+	ret = typec_port_set_pd(port, cap->pd);
+	if (ret) {
+		dev_err(&port->dev, "failed to link pd\n");
+		device_unregister(&port->dev);
+		return ERR_PTR(ret);
+	}
+
 	ret = typec_link_ports(port);
 	if (ret)
 		dev_warn(&port->dev, "failed to create symlinks (%d)\n", ret);
@@ -2141,6 +2285,7 @@  void typec_unregister_port(struct typec_port *port)
 {
 	if (!IS_ERR_OR_NULL(port)) {
 		typec_unlink_ports(port);
+		typec_port_set_pd(port, NULL);
 		device_unregister(&port->dev);
 	}
 }
@@ -2162,8 +2307,15 @@  static int __init typec_init(void)
 	if (ret)
 		goto err_unregister_mux_class;
 
+	ret = pd_init();
+	if (ret)
+		goto err_unregister_class;
+
 	return 0;
 
+err_unregister_class:
+	class_unregister(&typec_class);
+
 err_unregister_mux_class:
 	class_unregister(&typec_mux_class);
 
@@ -2176,6 +2328,7 @@  subsys_initcall(typec_init);
 
 static void __exit typec_exit(void)
 {
+	pd_exit();
 	class_unregister(&typec_class);
 	ida_destroy(&typec_index_ida);
 	bus_unregister(&typec_bus);
diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h
index 0f1bd6d19d67e..6c638fd8d85cb 100644
--- a/drivers/usb/typec/class.h
+++ b/drivers/usb/typec/class.h
@@ -33,6 +33,8 @@  struct typec_partner {
 	int				num_altmodes;
 	u16				pd_revision; /* 0300H = "3.0" */
 	enum usb_pd_svdm_ver		svdm_version;
+
+	struct pd			*pd;
 };
 
 struct typec_port {
@@ -40,6 +42,8 @@  struct typec_port {
 	struct device			dev;
 	struct ida			mode_ids;
 
+	struct pd			*pd;
+
 	int				prefer_role;
 	enum typec_data_role		data_role;
 	enum typec_role			pwr_role;
diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h
index 45e28d14ae56e..3855cd07925d3 100644
--- a/include/linux/usb/typec.h
+++ b/include/linux/usb/typec.h
@@ -22,6 +22,8 @@  struct typec_altmode_ops;
 struct fwnode_handle;
 struct device;
 
+struct pd;
+
 enum typec_port_type {
 	TYPEC_PORT_SRC,
 	TYPEC_PORT_SNK,
@@ -223,6 +225,8 @@  struct typec_partner_desc {
  * @pr_set: Set Power Role
  * @vconn_set: Source VCONN
  * @port_type_set: Set port type
+ * @pd_get: Get available USB Power Delivery Capabilities.
+ * @pd_set: Set USB Power Delivery Capabilities.
  */
 struct typec_operations {
 	int (*try_role)(struct typec_port *port, int role);
@@ -231,6 +235,8 @@  struct typec_operations {
 	int (*vconn_set)(struct typec_port *port, enum typec_role role);
 	int (*port_type_set)(struct typec_port *port,
 			     enum typec_port_type type);
+	struct pd **(*pd_get)(struct typec_port *port);
+	int (*pd_set)(struct typec_port *port, struct pd *pd);
 };
 
 enum usb_pd_svdm_ver {
@@ -250,6 +256,7 @@  enum usb_pd_svdm_ver {
  * @accessory: Supported Accessory Modes
  * @fwnode: Optional fwnode of the port
  * @driver_data: Private pointer for driver specific info
+ * @pd: Optional USB Power Delivery Support
  * @ops: Port operations vector
  *
  * Static capabilities of a single USB Type-C port.
@@ -267,6 +274,8 @@  struct typec_capability {
 	struct fwnode_handle	*fwnode;
 	void			*driver_data;
 
+	struct pd		*pd;
+
 	const struct typec_operations	*ops;
 };
 
@@ -318,4 +327,7 @@  void typec_partner_set_svdm_version(struct typec_partner *partner,
 				    enum usb_pd_svdm_ver svdm_version);
 int typec_get_negotiated_svdm_version(struct typec_port *port);
 
+int typec_port_set_pd(struct typec_port *port, struct pd *pd);
+int typec_partner_set_pd(struct typec_partner *partner, struct pd *pd);
+
 #endif /* __LINUX_USB_TYPEC_H */