forked from Minki/linux
usb: typec: USB Power Delivery helpers for ports and partners
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> Link: https://lore.kernel.org/r/20220502132058.86236-3-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
662a60102c
commit
a7cff92f06
@ -141,6 +141,14 @@ Description:
|
|||||||
- "reverse": CC2 orientation
|
- "reverse": CC2 orientation
|
||||||
- "unknown": Orientation cannot be determined.
|
- "unknown": Orientation cannot be determined.
|
||||||
|
|
||||||
|
What: /sys/class/typec/<port>/select_usb_power_delivery
|
||||||
|
Date: May 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/)
|
USB Type-C partner devices (eg. /sys/class/typec/port0-partner/)
|
||||||
|
|
||||||
What: /sys/class/typec/<port>-partner/accessory_mode
|
What: /sys/class/typec/<port>-partner/accessory_mode
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
#include "bus.h"
|
#include "bus.h"
|
||||||
#include "class.h"
|
#include "class.h"
|
||||||
|
#include "pd.h"
|
||||||
|
|
||||||
static DEFINE_IDA(typec_index_ida);
|
static DEFINE_IDA(typec_index_ida);
|
||||||
|
|
||||||
@ -720,6 +721,39 @@ void typec_partner_set_pd_revision(struct typec_partner *partner, u16 pd_revisio
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(typec_partner_set_pd_revision);
|
EXPORT_SYMBOL_GPL(typec_partner_set_pd_revision);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* typec_partner_set_usb_power_delivery - 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_usb_power_delivery(struct typec_partner *partner,
|
||||||
|
struct usb_power_delivery *pd)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (IS_ERR_OR_NULL(partner) || partner->pd == pd)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (pd) {
|
||||||
|
ret = usb_power_delivery_link_device(pd, &partner->dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
} else {
|
||||||
|
usb_power_delivery_unlink_device(partner->pd, &partner->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
partner->pd = pd;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(typec_partner_set_usb_power_delivery);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* typec_partner_set_num_altmodes - Set the number of available partner altmodes
|
* typec_partner_set_num_altmodes - Set the number of available partner altmodes
|
||||||
* @partner: The partner to be updated.
|
* @partner: The partner to be updated.
|
||||||
@ -1170,6 +1204,104 @@ EXPORT_SYMBOL_GPL(typec_unregister_cable);
|
|||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
/* USB Type-C ports */
|
/* USB Type-C ports */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* typec_port_set_usb_power_delivery - 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_usb_power_delivery(struct typec_port *port, struct usb_power_delivery *pd)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (IS_ERR_OR_NULL(port) || port->pd == pd)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (pd) {
|
||||||
|
ret = usb_power_delivery_link_device(pd, &port->dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
} else {
|
||||||
|
usb_power_delivery_unlink_device(port->pd, &port->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
port->pd = pd;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(typec_port_set_usb_power_delivery);
|
||||||
|
|
||||||
|
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 usb_power_delivery *pd;
|
||||||
|
|
||||||
|
if (!port->ops || !port->ops->pd_set)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
pd = usb_power_delivery_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 usb_power_delivery **pds;
|
||||||
|
struct usb_power_delivery *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] ", dev_name(&pd->dev));
|
||||||
|
else
|
||||||
|
ret += sysfs_emit(buf + ret, "%s ", dev_name(&pd->dev));
|
||||||
|
}
|
||||||
|
|
||||||
|
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[] = {
|
static const char * const typec_orientations[] = {
|
||||||
[TYPEC_ORIENTATION_NONE] = "unknown",
|
[TYPEC_ORIENTATION_NONE] = "unknown",
|
||||||
[TYPEC_ORIENTATION_NORMAL] = "normal",
|
[TYPEC_ORIENTATION_NORMAL] = "normal",
|
||||||
@ -1581,6 +1713,7 @@ static const struct attribute_group typec_group = {
|
|||||||
|
|
||||||
static const struct attribute_group *typec_groups[] = {
|
static const struct attribute_group *typec_groups[] = {
|
||||||
&typec_group,
|
&typec_group,
|
||||||
|
&pd_group,
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2123,6 +2256,13 @@ struct typec_port *typec_register_port(struct device *parent,
|
|||||||
return ERR_PTR(ret);
|
return ERR_PTR(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = typec_port_set_usb_power_delivery(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);
|
ret = typec_link_ports(port);
|
||||||
if (ret)
|
if (ret)
|
||||||
dev_warn(&port->dev, "failed to create symlinks (%d)\n", ret);
|
dev_warn(&port->dev, "failed to create symlinks (%d)\n", ret);
|
||||||
@ -2141,6 +2281,7 @@ void typec_unregister_port(struct typec_port *port)
|
|||||||
{
|
{
|
||||||
if (!IS_ERR_OR_NULL(port)) {
|
if (!IS_ERR_OR_NULL(port)) {
|
||||||
typec_unlink_ports(port);
|
typec_unlink_ports(port);
|
||||||
|
typec_port_set_usb_power_delivery(port, NULL);
|
||||||
device_unregister(&port->dev);
|
device_unregister(&port->dev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2162,8 +2303,15 @@ static int __init typec_init(void)
|
|||||||
if (ret)
|
if (ret)
|
||||||
goto err_unregister_mux_class;
|
goto err_unregister_mux_class;
|
||||||
|
|
||||||
|
ret = usb_power_delivery_init();
|
||||||
|
if (ret)
|
||||||
|
goto err_unregister_class;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err_unregister_class:
|
||||||
|
class_unregister(&typec_class);
|
||||||
|
|
||||||
err_unregister_mux_class:
|
err_unregister_mux_class:
|
||||||
class_unregister(&typec_mux_class);
|
class_unregister(&typec_mux_class);
|
||||||
|
|
||||||
@ -2176,6 +2324,7 @@ subsys_initcall(typec_init);
|
|||||||
|
|
||||||
static void __exit typec_exit(void)
|
static void __exit typec_exit(void)
|
||||||
{
|
{
|
||||||
|
usb_power_delivery_exit();
|
||||||
class_unregister(&typec_class);
|
class_unregister(&typec_class);
|
||||||
ida_destroy(&typec_index_ida);
|
ida_destroy(&typec_index_ida);
|
||||||
bus_unregister(&typec_bus);
|
bus_unregister(&typec_bus);
|
||||||
|
@ -33,6 +33,8 @@ struct typec_partner {
|
|||||||
int num_altmodes;
|
int num_altmodes;
|
||||||
u16 pd_revision; /* 0300H = "3.0" */
|
u16 pd_revision; /* 0300H = "3.0" */
|
||||||
enum usb_pd_svdm_ver svdm_version;
|
enum usb_pd_svdm_ver svdm_version;
|
||||||
|
|
||||||
|
struct usb_power_delivery *pd;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct typec_port {
|
struct typec_port {
|
||||||
@ -40,6 +42,8 @@ struct typec_port {
|
|||||||
struct device dev;
|
struct device dev;
|
||||||
struct ida mode_ids;
|
struct ida mode_ids;
|
||||||
|
|
||||||
|
struct usb_power_delivery *pd;
|
||||||
|
|
||||||
int prefer_role;
|
int prefer_role;
|
||||||
enum typec_data_role data_role;
|
enum typec_data_role data_role;
|
||||||
enum typec_role pwr_role;
|
enum typec_role pwr_role;
|
||||||
|
@ -22,6 +22,8 @@ struct typec_altmode_ops;
|
|||||||
struct fwnode_handle;
|
struct fwnode_handle;
|
||||||
struct device;
|
struct device;
|
||||||
|
|
||||||
|
struct usb_power_delivery;
|
||||||
|
|
||||||
enum typec_port_type {
|
enum typec_port_type {
|
||||||
TYPEC_PORT_SRC,
|
TYPEC_PORT_SRC,
|
||||||
TYPEC_PORT_SNK,
|
TYPEC_PORT_SNK,
|
||||||
@ -223,6 +225,8 @@ struct typec_partner_desc {
|
|||||||
* @pr_set: Set Power Role
|
* @pr_set: Set Power Role
|
||||||
* @vconn_set: Source VCONN
|
* @vconn_set: Source VCONN
|
||||||
* @port_type_set: Set port type
|
* @port_type_set: Set port type
|
||||||
|
* @pd_get: Get available USB Power Delivery Capabilities.
|
||||||
|
* @pd_set: Set USB Power Delivery Capabilities.
|
||||||
*/
|
*/
|
||||||
struct typec_operations {
|
struct typec_operations {
|
||||||
int (*try_role)(struct typec_port *port, int role);
|
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 (*vconn_set)(struct typec_port *port, enum typec_role role);
|
||||||
int (*port_type_set)(struct typec_port *port,
|
int (*port_type_set)(struct typec_port *port,
|
||||||
enum typec_port_type type);
|
enum typec_port_type type);
|
||||||
|
struct usb_power_delivery **(*pd_get)(struct typec_port *port);
|
||||||
|
int (*pd_set)(struct typec_port *port, struct usb_power_delivery *pd);
|
||||||
};
|
};
|
||||||
|
|
||||||
enum usb_pd_svdm_ver {
|
enum usb_pd_svdm_ver {
|
||||||
@ -250,6 +256,7 @@ enum usb_pd_svdm_ver {
|
|||||||
* @accessory: Supported Accessory Modes
|
* @accessory: Supported Accessory Modes
|
||||||
* @fwnode: Optional fwnode of the port
|
* @fwnode: Optional fwnode of the port
|
||||||
* @driver_data: Private pointer for driver specific info
|
* @driver_data: Private pointer for driver specific info
|
||||||
|
* @pd: Optional USB Power Delivery Support
|
||||||
* @ops: Port operations vector
|
* @ops: Port operations vector
|
||||||
*
|
*
|
||||||
* Static capabilities of a single USB Type-C port.
|
* Static capabilities of a single USB Type-C port.
|
||||||
@ -267,6 +274,8 @@ struct typec_capability {
|
|||||||
struct fwnode_handle *fwnode;
|
struct fwnode_handle *fwnode;
|
||||||
void *driver_data;
|
void *driver_data;
|
||||||
|
|
||||||
|
struct usb_power_delivery *pd;
|
||||||
|
|
||||||
const struct typec_operations *ops;
|
const struct typec_operations *ops;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -318,4 +327,8 @@ void typec_partner_set_svdm_version(struct typec_partner *partner,
|
|||||||
enum usb_pd_svdm_ver svdm_version);
|
enum usb_pd_svdm_ver svdm_version);
|
||||||
int typec_get_negotiated_svdm_version(struct typec_port *port);
|
int typec_get_negotiated_svdm_version(struct typec_port *port);
|
||||||
|
|
||||||
|
int typec_port_set_usb_power_delivery(struct typec_port *port, struct usb_power_delivery *pd);
|
||||||
|
int typec_partner_set_usb_power_delivery(struct typec_partner *partner,
|
||||||
|
struct usb_power_delivery *pd);
|
||||||
|
|
||||||
#endif /* __LINUX_USB_TYPEC_H */
|
#endif /* __LINUX_USB_TYPEC_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user