From 662a60102c122e44fdaf5c826f7f415eb57d48ad Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 2 May 2022 16:20:56 +0300 Subject: usb: typec: Separate USB Power Delivery from USB Type-C Introducing a small device class for USB Power Delivery. The idea with it is that we do not mix any more USB Power Delivery information into the USB Type-C connectors only. This separation will make it possible to register USB Power Delivery devices also from other places, for example from USB Type-C Bridges (see USB Type-C Bridge Specification). The device class will not always deal with only the messages and objects that were negotiated with the partner, but instead messages and objects that can be used in the negotiation. That allows the USB PD devices to be shared and reconfigured. The ports can decide which objects are to be advertised to the partner before the contract is negotiated. It is also possible to allow the user space to make that decision if needed. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20220502132058.86236-2-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/pd.h | 38 ++++++++++++++++++++++++++++++++++++++ include/linux/usb/typec.h | 10 ++++++++++ 2 files changed, 48 insertions(+) (limited to 'include') diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h index 96b7ff66f074..c59fb79a42e8 100644 --- a/include/linux/usb/pd.h +++ b/include/linux/usb/pd.h @@ -495,4 +495,42 @@ static inline unsigned int rdo_max_power(u32 rdo) #define PD_P_SNK_STDBY_MW 2500 /* 2500 mW */ +#if IS_ENABLED(CONFIG_TYPEC) + +struct usb_power_delivery; + +/** + * usb_power_delivery_desc - USB Power Delivery Descriptor + * @revision: USB Power Delivery Specification Revision + * @version: USB Power Delivery Specicication Version - optional + */ +struct usb_power_delivery_desc { + u16 revision; + u16 version; +}; + +/** + * usb_power_delivery_capabilities_desc - Description of USB Power Delivery Capabilities Message + * @pdo: The Power Data Objects in the Capability Message + * @role: Power role of the capabilities + */ +struct usb_power_delivery_capabilities_desc { + u32 pdo[PDO_MAX_OBJECTS]; + enum typec_role role; +}; + +struct usb_power_delivery_capabilities * +usb_power_delivery_register_capabilities(struct usb_power_delivery *pd, + struct usb_power_delivery_capabilities_desc *desc); +void usb_power_delivery_unregister_capabilities(struct usb_power_delivery_capabilities *cap); + +struct usb_power_delivery *usb_power_delivery_register(struct device *parent, + struct usb_power_delivery_desc *desc); +void usb_power_delivery_unregister(struct usb_power_delivery *pd); + +int usb_power_delivery_link_device(struct usb_power_delivery *pd, struct device *dev); +void usb_power_delivery_unlink_device(struct usb_power_delivery *pd, struct device *dev); + +#endif /* CONFIG_TYPEC */ + #endif /* __LINUX_USB_PD_H */ diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index fdf737d48b3b..45e28d14ae56 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -52,6 +52,16 @@ enum typec_role { TYPEC_SOURCE, }; +static inline int is_sink(enum typec_role role) +{ + return role == TYPEC_SINK; +} + +static inline int is_source(enum typec_role role) +{ + return role == TYPEC_SOURCE; +} + enum typec_pwr_opmode { TYPEC_PWR_MODE_USB, TYPEC_PWR_MODE_1_5A, -- cgit v1.2.3 From a7cff92f0635c794e2198a69a7ff4ecfe0decab9 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 2 May 2022 16:20:57 +0300 Subject: 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 Link: https://lore.kernel.org/r/20220502132058.86236-3-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-class-typec | 8 ++ drivers/usb/typec/class.c | 149 ++++++++++++++++++++++++++++ drivers/usb/typec/class.h | 4 + include/linux/usb/typec.h | 13 +++ 4 files changed, 174 insertions(+) (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-class-typec b/Documentation/ABI/testing/sysfs-class-typec index 75088ecad202..281b995beb05 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//select_usb_power_delivery +Date: May 2022 +Contact: Heikki Krogerus +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/-partner/accessory_mode diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index ee0e520707dd..bbc46b14f99a 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); @@ -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); +/** + * 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 * @partner: The partner to be updated. @@ -1170,6 +1204,104 @@ EXPORT_SYMBOL_GPL(typec_unregister_cable); /* ------------------------------------------------------------------------- */ /* 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[] = { [TYPEC_ORIENTATION_NONE] = "unknown", [TYPEC_ORIENTATION_NORMAL] = "normal", @@ -1581,6 +1713,7 @@ static const struct attribute_group typec_group = { static const struct attribute_group *typec_groups[] = { &typec_group, + &pd_group, NULL }; @@ -2123,6 +2256,13 @@ struct typec_port *typec_register_port(struct device *parent, 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); if (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)) { typec_unlink_ports(port); + typec_port_set_usb_power_delivery(port, NULL); device_unregister(&port->dev); } } @@ -2162,8 +2303,15 @@ static int __init typec_init(void) if (ret) goto err_unregister_mux_class; + ret = usb_power_delivery_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 +2324,7 @@ subsys_initcall(typec_init); static void __exit typec_exit(void) { + usb_power_delivery_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 0f1bd6d19d67..b531f9853bc0 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 usb_power_delivery *pd; }; struct typec_port { @@ -40,6 +42,8 @@ struct typec_port { struct device dev; struct ida mode_ids; + struct usb_power_delivery *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 45e28d14ae56..7751bedcae5d 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 usb_power_delivery; + 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 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 { @@ -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 usb_power_delivery *pd; + 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); 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 */ -- cgit v1.2.3 From e146caf303493c4f2458173d7f1598b76a9b1396 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Fri, 6 May 2022 19:18:07 +0300 Subject: usb: Avoid extra usb SET_SEL requests when enabling link power management The host needs to tell the device the exit latencies using the SET_SEL request before device initiated link powermanagement can be enabled. The exit latency values do not change after enumeration, it's enough to set them once. So do like Windows 10 and issue the SET_SEL request once just before setting the configuration. This is also the sequence described in USB 3.2 specs "9.1.2 Bus enumeration". SET_SEL is issued once before the Set Configuration request, and won't be cleared by the Set Configuration, Set Interface or ClearFeature (STALL) requests. Only warm reset, hot reset, set Address 0 clears the exit latencies. See USB 3.2 section 9.4.14 Table 9-10 Device parameters and events Add udev->lpm_devinit_allow, and set it if SET_SEL was successful. If not set, then don't try to enable device initiated LPM We used to issue a SET_SEL request every time lpm is enabled for either U1 or U2 link states, meaning a SET_SEL was issued twice after every Set Configuration and Set Interface requests, easily accumulating to over 15 SET_SEL requets during a USB3 webcam enumeration. Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20220506161807.3369439-1-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 60 ++++++++++++++++++-------------------------------- include/linux/usb.h | 2 ++ 2 files changed, 23 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index ba406b8d688d..b7f66dcd1fe0 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3947,7 +3947,7 @@ static const char * const usb3_lpm_names[] = { * This function will fail if the SEL or PEL values for udev are greater than * the maximum allowed values for the link state to be enabled. */ -static int usb_req_set_sel(struct usb_device *udev, enum usb3_link_state state) +static int usb_req_set_sel(struct usb_device *udev) { struct usb_set_sel_req *sel_values; unsigned long long u1_sel; @@ -3956,7 +3956,7 @@ static int usb_req_set_sel(struct usb_device *udev, enum usb3_link_state state) unsigned long long u2_pel; int ret; - if (udev->state != USB_STATE_CONFIGURED) + if (!udev->parent || udev->speed < USB_SPEED_SUPER || !udev->lpm_capable) return 0; /* Convert SEL and PEL stored in ns to us */ @@ -3973,34 +3973,14 @@ static int usb_req_set_sel(struct usb_device *udev, enum usb3_link_state state) * latency for the link state, and could start a device-initiated * U1/U2 when the exit latencies are too high. */ - if ((state == USB3_LPM_U1 && - (u1_sel > USB3_LPM_MAX_U1_SEL_PEL || - u1_pel > USB3_LPM_MAX_U1_SEL_PEL)) || - (state == USB3_LPM_U2 && - (u2_sel > USB3_LPM_MAX_U2_SEL_PEL || - u2_pel > USB3_LPM_MAX_U2_SEL_PEL))) { - dev_dbg(&udev->dev, "Device-initiated %s disabled due to long SEL %llu us or PEL %llu us\n", - usb3_lpm_names[state], u1_sel, u1_pel); + if (u1_sel > USB3_LPM_MAX_U1_SEL_PEL || + u1_pel > USB3_LPM_MAX_U1_SEL_PEL || + u2_sel > USB3_LPM_MAX_U2_SEL_PEL || + u2_pel > USB3_LPM_MAX_U2_SEL_PEL) { + dev_dbg(&udev->dev, "Device-initiated U1/U2 disabled due to long SEL or PEL\n"); return -EINVAL; } - /* - * If we're enabling device-initiated LPM for one link state, - * but the other link state has a too high SEL or PEL value, - * just set those values to the max in the Set SEL request. - */ - if (u1_sel > USB3_LPM_MAX_U1_SEL_PEL) - u1_sel = USB3_LPM_MAX_U1_SEL_PEL; - - if (u1_pel > USB3_LPM_MAX_U1_SEL_PEL) - u1_pel = USB3_LPM_MAX_U1_SEL_PEL; - - if (u2_sel > USB3_LPM_MAX_U2_SEL_PEL) - u2_sel = USB3_LPM_MAX_U2_SEL_PEL; - - if (u2_pel > USB3_LPM_MAX_U2_SEL_PEL) - u2_pel = USB3_LPM_MAX_U2_SEL_PEL; - /* * usb_enable_lpm() can be called as part of a failed device reset, * which may be initiated by an error path of a mass storage driver. @@ -4022,6 +4002,10 @@ static int usb_req_set_sel(struct usb_device *udev, enum usb3_link_state state) sel_values, sizeof *(sel_values), USB_CTRL_SET_TIMEOUT); kfree(sel_values); + + if (ret > 0) + udev->lpm_devinit_allow = 1; + return ret; } @@ -4137,6 +4121,9 @@ static bool usb_device_may_initiate_lpm(struct usb_device *udev, unsigned int sel; /* us */ int i, j; + if (!udev->lpm_devinit_allow) + return false; + if (state == USB3_LPM_U1) sel = DIV_ROUND_UP(udev->u1_params.sel, 1000); else if (state == USB3_LPM_U2) @@ -4185,7 +4172,7 @@ static bool usb_device_may_initiate_lpm(struct usb_device *udev, static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev, enum usb3_link_state state) { - int timeout, ret; + int timeout; __u8 u1_mel = udev->bos->ss_cap->bU1devExitLat; __le16 u2_mel = udev->bos->ss_cap->bU2DevExitLat; @@ -4197,17 +4184,6 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev, (state == USB3_LPM_U2 && u2_mel == 0)) return; - /* - * First, let the device know about the exit latencies - * associated with the link state we're about to enable. - */ - ret = usb_req_set_sel(udev, state); - if (ret < 0) { - dev_warn(&udev->dev, "Set SEL for device-initiated %s failed.\n", - usb3_lpm_names[state]); - return; - } - /* We allow the host controller to set the U1/U2 timeout internally * first, so that it can change its schedule to account for the * additional latency to send data to a device in a lower power @@ -4487,6 +4463,11 @@ static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port, return 0; } +static int usb_req_set_sel(struct usb_device *udev) +{ + return 0; +} + #endif /* CONFIG_PM */ /* @@ -5012,6 +4993,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, udev->lpm_capable = usb_device_supports_lpm(udev); udev->lpm_disable_count = 1; usb_set_lpm_parameters(udev); + usb_req_set_sel(udev); } } diff --git a/include/linux/usb.h b/include/linux/usb.h index 60bee864d897..f7a9914fc97f 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -584,6 +584,7 @@ struct usb3_lpm_parameters { * @authenticated: Crypto authentication passed * @wusb: device is Wireless USB * @lpm_capable: device supports LPM + * @lpm_devinit_allow: Allow USB3 device initiated LPM, exit latency is in range * @usb2_hw_lpm_capable: device can perform USB2 hardware LPM * @usb2_hw_lpm_besl_capable: device can perform USB2 hardware BESL LPM * @usb2_hw_lpm_enabled: USB2 hardware LPM is enabled @@ -666,6 +667,7 @@ struct usb_device { unsigned authenticated:1; unsigned wusb:1; unsigned lpm_capable:1; + unsigned lpm_devinit_allow:1; unsigned usb2_hw_lpm_capable:1; unsigned usb2_hw_lpm_besl_capable:1; unsigned usb2_hw_lpm_enabled:1; -- cgit v1.2.3 From a37599ebfb656c2af4ca119de556eba29b6926d6 Mon Sep 17 00:00:00 2001 From: Prashant Malani Date: Wed, 15 Jun 2022 17:20:18 +0000 Subject: usb: typec: mux: Add CONFIG guards for functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are some drivers that can use the Type C mux API, but don't have to. Introduce CONFIG guards for the mux functions so that drivers can include the header file and not run into compilation errors on systems which don't have CONFIG_TYPEC enabled. When CONFIG_TYPEC is not enabled, the Type C mux functions will be stub versions of the original calls. Reported-by: kernel test robot Reviewed-by: NĂ­colas F. R. A. Prado Reviewed-by: Heikki Krogerus Reviewed-by: AngeloGioacchino Del Regno Tested-by: NĂ­colas F. R. A. Prado Signed-off-by: Prashant Malani Link: https://lore.kernel.org/r/20220615172129.1314056-3-pmalani@chromium.org Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/typec_mux.h | 44 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/usb/typec_mux.h b/include/linux/usb/typec_mux.h index ee57781dcf28..9292f0e07846 100644 --- a/include/linux/usb/typec_mux.h +++ b/include/linux/usb/typec_mux.h @@ -58,17 +58,13 @@ struct typec_mux_desc { void *drvdata; }; +#if IS_ENABLED(CONFIG_TYPEC) + struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode, const struct typec_altmode_desc *desc); void typec_mux_put(struct typec_mux *mux); int typec_mux_set(struct typec_mux *mux, struct typec_mux_state *state); -static inline struct typec_mux * -typec_mux_get(struct device *dev, const struct typec_altmode_desc *desc) -{ - return fwnode_typec_mux_get(dev_fwnode(dev), desc); -} - struct typec_mux_dev * typec_mux_register(struct device *parent, const struct typec_mux_desc *desc); void typec_mux_unregister(struct typec_mux_dev *mux); @@ -76,4 +72,40 @@ void typec_mux_unregister(struct typec_mux_dev *mux); void typec_mux_set_drvdata(struct typec_mux_dev *mux, void *data); void *typec_mux_get_drvdata(struct typec_mux_dev *mux); +#else + +static inline struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode, + const struct typec_altmode_desc *desc) +{ + return NULL; +} + +static inline void typec_mux_put(struct typec_mux *mux) {} + +static inline int typec_mux_set(struct typec_mux *mux, struct typec_mux_state *state) +{ + return 0; +} + +static inline struct typec_mux_dev * +typec_mux_register(struct device *parent, const struct typec_mux_desc *desc) +{ + return ERR_PTR(-EOPNOTSUPP); +} +static inline void typec_mux_unregister(struct typec_mux_dev *mux) {} + +static inline void typec_mux_set_drvdata(struct typec_mux_dev *mux, void *data) {} +static inline void *typec_mux_get_drvdata(struct typec_mux_dev *mux) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +#endif /* CONFIG_TYPEC */ + +static inline struct typec_mux * +typec_mux_get(struct device *dev, const struct typec_altmode_desc *desc) +{ + return fwnode_typec_mux_get(dev_fwnode(dev), desc); +} + #endif /* __USB_TYPEC_MUX */ -- cgit v1.2.3 From ae92b1c84306a68c361d90b46ad3acef99c10e29 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 28 Jun 2022 10:46:24 +0100 Subject: usb: typec_altmode: add a missing "@" at a kernel-doc parameter Without that, the parameter is not properly parsed: include/linux/usb/typec_altmode.h:132: warning: Function parameter or member 'altmode' not described in 'typec_altmode_get_orientation' Signed-off-by: Mauro Carvalho Chehab Acked-by: Heikki Krogerus Link: https://lore.kernel.org/r/70dc4c5d744cf1fe9a0efe6b85deaa0489628282.1656409369.git.mchehab@kernel.org Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/typec_altmode.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_altmode.h index 65933cbe9129..350d49012659 100644 --- a/include/linux/usb/typec_altmode.h +++ b/include/linux/usb/typec_altmode.h @@ -124,7 +124,7 @@ struct typec_altmode *typec_match_altmode(struct typec_altmode **altmodes, /** * typec_altmode_get_orientation - Get cable plug orientation - * altmode: Handle to the alternate mode + * @altmode: Handle to the alternate mode */ static inline enum typec_orientation typec_altmode_get_orientation(struct typec_altmode *altmode) -- cgit v1.2.3 From 7963d4d710112bc457f99bdb56608211e561190e Mon Sep 17 00:00:00 2001 From: Xin Ji Date: Wed, 6 Jul 2022 16:34:31 +0800 Subject: usb: typec: tcpci: move tcpci.h to include/linux/usb/ USB PD controllers which consisting of a microcontroller (acting as the TCPM) and a port controller (TCPC) - may require that the driver for the PD controller accesses directly also the on-chip port controller in some cases. Move tcpci.h to include/linux/usb/ is convenience access TCPC registers. Reviewed-by: Heikki Krogerus Signed-off-by: Xin Ji Link: https://lore.kernel.org/r/20220706083433.2415524-1-xji@analogixsemi.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpci.c | 3 +- drivers/usb/typec/tcpm/tcpci.h | 209 -------------------------------- drivers/usb/typec/tcpm/tcpci_maxim.c | 3 +- drivers/usb/typec/tcpm/tcpci_mt6360.c | 3 +- drivers/usb/typec/tcpm/tcpci_rt1711h.c | 2 +- include/linux/usb/tcpci.h | 210 +++++++++++++++++++++++++++++++++ 6 files changed, 214 insertions(+), 216 deletions(-) delete mode 100644 drivers/usb/typec/tcpm/tcpci.h create mode 100644 include/linux/usb/tcpci.h (limited to 'include') diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c index f33e08eb7670..812784702d53 100644 --- a/drivers/usb/typec/tcpm/tcpci.c +++ b/drivers/usb/typec/tcpm/tcpci.c @@ -13,11 +13,10 @@ #include #include #include +#include #include #include -#include "tcpci.h" - #define PD_RETRY_COUNT_DEFAULT 3 #define PD_RETRY_COUNT_3_0_OR_HIGHER 2 #define AUTO_DISCHARGE_DEFAULT_THRESHOLD_MV 3500 diff --git a/drivers/usb/typec/tcpm/tcpci.h b/drivers/usb/typec/tcpm/tcpci.h deleted file mode 100644 index b2edd45f13c6..000000000000 --- a/drivers/usb/typec/tcpm/tcpci.h +++ /dev/null @@ -1,209 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright 2015-2017 Google, Inc - * - * USB Type-C Port Controller Interface. - */ - -#ifndef __LINUX_USB_TCPCI_H -#define __LINUX_USB_TCPCI_H - -#include - -#define TCPC_VENDOR_ID 0x0 -#define TCPC_PRODUCT_ID 0x2 -#define TCPC_BCD_DEV 0x4 -#define TCPC_TC_REV 0x6 -#define TCPC_PD_REV 0x8 -#define TCPC_PD_INT_REV 0xa - -#define TCPC_ALERT 0x10 -#define TCPC_ALERT_EXTND BIT(14) -#define TCPC_ALERT_EXTENDED_STATUS BIT(13) -#define TCPC_ALERT_VBUS_DISCNCT BIT(11) -#define TCPC_ALERT_RX_BUF_OVF BIT(10) -#define TCPC_ALERT_FAULT BIT(9) -#define TCPC_ALERT_V_ALARM_LO BIT(8) -#define TCPC_ALERT_V_ALARM_HI BIT(7) -#define TCPC_ALERT_TX_SUCCESS BIT(6) -#define TCPC_ALERT_TX_DISCARDED BIT(5) -#define TCPC_ALERT_TX_FAILED BIT(4) -#define TCPC_ALERT_RX_HARD_RST BIT(3) -#define TCPC_ALERT_RX_STATUS BIT(2) -#define TCPC_ALERT_POWER_STATUS BIT(1) -#define TCPC_ALERT_CC_STATUS BIT(0) - -#define TCPC_ALERT_MASK 0x12 -#define TCPC_POWER_STATUS_MASK 0x14 -#define TCPC_FAULT_STATUS_MASK 0x15 - -#define TCPC_EXTENDED_STATUS_MASK 0x16 -#define TCPC_EXTENDED_STATUS_MASK_VSAFE0V BIT(0) - -#define TCPC_ALERT_EXTENDED_MASK 0x17 -#define TCPC_SINK_FAST_ROLE_SWAP BIT(0) - -#define TCPC_CONFIG_STD_OUTPUT 0x18 - -#define TCPC_TCPC_CTRL 0x19 -#define TCPC_TCPC_CTRL_ORIENTATION BIT(0) -#define PLUG_ORNT_CC1 0 -#define PLUG_ORNT_CC2 1 -#define TCPC_TCPC_CTRL_BIST_TM BIT(1) -#define TCPC_TCPC_CTRL_EN_LK4CONN_ALRT BIT(6) - -#define TCPC_EXTENDED_STATUS 0x20 -#define TCPC_EXTENDED_STATUS_VSAFE0V BIT(0) - -#define TCPC_ROLE_CTRL 0x1a -#define TCPC_ROLE_CTRL_DRP BIT(6) -#define TCPC_ROLE_CTRL_RP_VAL_SHIFT 4 -#define TCPC_ROLE_CTRL_RP_VAL_MASK 0x3 -#define TCPC_ROLE_CTRL_RP_VAL_DEF 0x0 -#define TCPC_ROLE_CTRL_RP_VAL_1_5 0x1 -#define TCPC_ROLE_CTRL_RP_VAL_3_0 0x2 -#define TCPC_ROLE_CTRL_CC2_SHIFT 2 -#define TCPC_ROLE_CTRL_CC2_MASK 0x3 -#define TCPC_ROLE_CTRL_CC1_SHIFT 0 -#define TCPC_ROLE_CTRL_CC1_MASK 0x3 -#define TCPC_ROLE_CTRL_CC_RA 0x0 -#define TCPC_ROLE_CTRL_CC_RP 0x1 -#define TCPC_ROLE_CTRL_CC_RD 0x2 -#define TCPC_ROLE_CTRL_CC_OPEN 0x3 - -#define TCPC_FAULT_CTRL 0x1b - -#define TCPC_POWER_CTRL 0x1c -#define TCPC_POWER_CTRL_VCONN_ENABLE BIT(0) -#define TCPC_POWER_CTRL_BLEED_DISCHARGE BIT(3) -#define TCPC_POWER_CTRL_AUTO_DISCHARGE BIT(4) -#define TCPC_DIS_VOLT_ALRM BIT(5) -#define TCPC_POWER_CTRL_VBUS_VOLT_MON BIT(6) -#define TCPC_FAST_ROLE_SWAP_EN BIT(7) - -#define TCPC_CC_STATUS 0x1d -#define TCPC_CC_STATUS_TOGGLING BIT(5) -#define TCPC_CC_STATUS_TERM BIT(4) -#define TCPC_CC_STATUS_TERM_RP 0 -#define TCPC_CC_STATUS_TERM_RD 1 -#define TCPC_CC_STATE_SRC_OPEN 0 -#define TCPC_CC_STATUS_CC2_SHIFT 2 -#define TCPC_CC_STATUS_CC2_MASK 0x3 -#define TCPC_CC_STATUS_CC1_SHIFT 0 -#define TCPC_CC_STATUS_CC1_MASK 0x3 - -#define TCPC_POWER_STATUS 0x1e -#define TCPC_POWER_STATUS_DBG_ACC_CON BIT(7) -#define TCPC_POWER_STATUS_UNINIT BIT(6) -#define TCPC_POWER_STATUS_SOURCING_VBUS BIT(4) -#define TCPC_POWER_STATUS_VBUS_DET BIT(3) -#define TCPC_POWER_STATUS_VBUS_PRES BIT(2) -#define TCPC_POWER_STATUS_VCONN_PRES BIT(1) -#define TCPC_POWER_STATUS_SINKING_VBUS BIT(0) - -#define TCPC_FAULT_STATUS 0x1f - -#define TCPC_ALERT_EXTENDED 0x21 - -#define TCPC_COMMAND 0x23 -#define TCPC_CMD_WAKE_I2C 0x11 -#define TCPC_CMD_DISABLE_VBUS_DETECT 0x22 -#define TCPC_CMD_ENABLE_VBUS_DETECT 0x33 -#define TCPC_CMD_DISABLE_SINK_VBUS 0x44 -#define TCPC_CMD_SINK_VBUS 0x55 -#define TCPC_CMD_DISABLE_SRC_VBUS 0x66 -#define TCPC_CMD_SRC_VBUS_DEFAULT 0x77 -#define TCPC_CMD_SRC_VBUS_HIGH 0x88 -#define TCPC_CMD_LOOK4CONNECTION 0x99 -#define TCPC_CMD_RXONEMORE 0xAA -#define TCPC_CMD_I2C_IDLE 0xFF - -#define TCPC_DEV_CAP_1 0x24 -#define TCPC_DEV_CAP_2 0x26 -#define TCPC_STD_INPUT_CAP 0x28 -#define TCPC_STD_OUTPUT_CAP 0x29 - -#define TCPC_MSG_HDR_INFO 0x2e -#define TCPC_MSG_HDR_INFO_DATA_ROLE BIT(3) -#define TCPC_MSG_HDR_INFO_PWR_ROLE BIT(0) -#define TCPC_MSG_HDR_INFO_REV_SHIFT 1 -#define TCPC_MSG_HDR_INFO_REV_MASK 0x3 - -#define TCPC_RX_DETECT 0x2f -#define TCPC_RX_DETECT_HARD_RESET BIT(5) -#define TCPC_RX_DETECT_SOP BIT(0) -#define TCPC_RX_DETECT_SOP1 BIT(1) -#define TCPC_RX_DETECT_SOP2 BIT(2) -#define TCPC_RX_DETECT_DBG1 BIT(3) -#define TCPC_RX_DETECT_DBG2 BIT(4) - -#define TCPC_RX_BYTE_CNT 0x30 -#define TCPC_RX_BUF_FRAME_TYPE 0x31 -#define TCPC_RX_BUF_FRAME_TYPE_SOP 0 -#define TCPC_RX_HDR 0x32 -#define TCPC_RX_DATA 0x34 /* through 0x4f */ - -#define TCPC_TRANSMIT 0x50 -#define TCPC_TRANSMIT_RETRY_SHIFT 4 -#define TCPC_TRANSMIT_RETRY_MASK 0x3 -#define TCPC_TRANSMIT_TYPE_SHIFT 0 -#define TCPC_TRANSMIT_TYPE_MASK 0x7 - -#define TCPC_TX_BYTE_CNT 0x51 -#define TCPC_TX_HDR 0x52 -#define TCPC_TX_DATA 0x54 /* through 0x6f */ - -#define TCPC_VBUS_VOLTAGE 0x70 -#define TCPC_VBUS_VOLTAGE_MASK 0x3ff -#define TCPC_VBUS_VOLTAGE_LSB_MV 25 -#define TCPC_VBUS_SINK_DISCONNECT_THRESH 0x72 -#define TCPC_VBUS_SINK_DISCONNECT_THRESH_LSB_MV 25 -#define TCPC_VBUS_SINK_DISCONNECT_THRESH_MAX 0x3ff -#define TCPC_VBUS_STOP_DISCHARGE_THRESH 0x74 -#define TCPC_VBUS_VOLTAGE_ALARM_HI_CFG 0x76 -#define TCPC_VBUS_VOLTAGE_ALARM_LO_CFG 0x78 - -/* I2C_WRITE_BYTE_COUNT + 1 when TX_BUF_BYTE_x is only accessible I2C_WRITE_BYTE_COUNT */ -#define TCPC_TRANSMIT_BUFFER_MAX_LEN 31 - -struct tcpci; - -/* - * @TX_BUF_BYTE_x_hidden: - * optional; Set when TX_BUF_BYTE_x can only be accessed through I2C_WRITE_BYTE_COUNT. - * @frs_sourcing_vbus: - * Optional; Callback to perform chip specific operations when FRS - * is sourcing vbus. - * @auto_discharge_disconnect: - * Optional; Enables TCPC to autonously discharge vbus on disconnect. - * @vbus_vsafe0v: - * optional; Set when TCPC can detect whether vbus is at VSAFE0V. - * @set_partner_usb_comm_capable: - * Optional; The USB Communications Capable bit indicates if port - * partner is capable of communication over the USB data lines - * (e.g. D+/- or SS Tx/Rx). Called to notify the status of the bit. - */ -struct tcpci_data { - struct regmap *regmap; - unsigned char TX_BUF_BYTE_x_hidden:1; - unsigned char auto_discharge_disconnect:1; - unsigned char vbus_vsafe0v:1; - - int (*init)(struct tcpci *tcpci, struct tcpci_data *data); - int (*set_vconn)(struct tcpci *tcpci, struct tcpci_data *data, - bool enable); - int (*start_drp_toggling)(struct tcpci *tcpci, struct tcpci_data *data, - enum typec_cc_status cc); - int (*set_vbus)(struct tcpci *tcpci, struct tcpci_data *data, bool source, bool sink); - void (*frs_sourcing_vbus)(struct tcpci *tcpci, struct tcpci_data *data); - void (*set_partner_usb_comm_capable)(struct tcpci *tcpci, struct tcpci_data *data, - bool capable); -}; - -struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data); -void tcpci_unregister_port(struct tcpci *tcpci); -irqreturn_t tcpci_irq(struct tcpci *tcpci); - -struct tcpm_port; -struct tcpm_port *tcpci_get_tcpm_port(struct tcpci *tcpci); -#endif /* __LINUX_USB_TCPCI_H */ diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.c b/drivers/usb/typec/tcpm/tcpci_maxim.c index df2505570f07..4b6705f3d7b7 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim.c +++ b/drivers/usb/typec/tcpm/tcpci_maxim.c @@ -11,11 +11,10 @@ #include #include #include +#include #include #include -#include "tcpci.h" - #define PD_ACTIVITY_TIMEOUT_MS 10000 #define TCPC_VENDOR_ALERT 0x80 diff --git a/drivers/usb/typec/tcpm/tcpci_mt6360.c b/drivers/usb/typec/tcpm/tcpci_mt6360.c index 8a952eaf9016..1b7c31278ebb 100644 --- a/drivers/usb/typec/tcpm/tcpci_mt6360.c +++ b/drivers/usb/typec/tcpm/tcpci_mt6360.c @@ -11,10 +11,9 @@ #include #include #include +#include #include -#include "tcpci.h" - #define MT6360_REG_PHYCTRL1 0x80 #define MT6360_REG_PHYCTRL3 0x82 #define MT6360_REG_PHYCTRL7 0x86 diff --git a/drivers/usb/typec/tcpm/tcpci_rt1711h.c b/drivers/usb/typec/tcpm/tcpci_rt1711h.c index b56a0880a044..3291ca4948da 100644 --- a/drivers/usb/typec/tcpm/tcpci_rt1711h.c +++ b/drivers/usb/typec/tcpm/tcpci_rt1711h.c @@ -10,9 +10,9 @@ #include #include #include +#include #include #include -#include "tcpci.h" #define RT1711H_VID 0x29CF #define RT1711H_PID 0x1711 diff --git a/include/linux/usb/tcpci.h b/include/linux/usb/tcpci.h new file mode 100644 index 000000000000..20c0bedb8ec8 --- /dev/null +++ b/include/linux/usb/tcpci.h @@ -0,0 +1,210 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2015-2017 Google, Inc + * + * USB Type-C Port Controller Interface. + */ + +#ifndef __LINUX_USB_TCPCI_H +#define __LINUX_USB_TCPCI_H + +#include +#include + +#define TCPC_VENDOR_ID 0x0 +#define TCPC_PRODUCT_ID 0x2 +#define TCPC_BCD_DEV 0x4 +#define TCPC_TC_REV 0x6 +#define TCPC_PD_REV 0x8 +#define TCPC_PD_INT_REV 0xa + +#define TCPC_ALERT 0x10 +#define TCPC_ALERT_EXTND BIT(14) +#define TCPC_ALERT_EXTENDED_STATUS BIT(13) +#define TCPC_ALERT_VBUS_DISCNCT BIT(11) +#define TCPC_ALERT_RX_BUF_OVF BIT(10) +#define TCPC_ALERT_FAULT BIT(9) +#define TCPC_ALERT_V_ALARM_LO BIT(8) +#define TCPC_ALERT_V_ALARM_HI BIT(7) +#define TCPC_ALERT_TX_SUCCESS BIT(6) +#define TCPC_ALERT_TX_DISCARDED BIT(5) +#define TCPC_ALERT_TX_FAILED BIT(4) +#define TCPC_ALERT_RX_HARD_RST BIT(3) +#define TCPC_ALERT_RX_STATUS BIT(2) +#define TCPC_ALERT_POWER_STATUS BIT(1) +#define TCPC_ALERT_CC_STATUS BIT(0) + +#define TCPC_ALERT_MASK 0x12 +#define TCPC_POWER_STATUS_MASK 0x14 +#define TCPC_FAULT_STATUS_MASK 0x15 + +#define TCPC_EXTENDED_STATUS_MASK 0x16 +#define TCPC_EXTENDED_STATUS_MASK_VSAFE0V BIT(0) + +#define TCPC_ALERT_EXTENDED_MASK 0x17 +#define TCPC_SINK_FAST_ROLE_SWAP BIT(0) + +#define TCPC_CONFIG_STD_OUTPUT 0x18 + +#define TCPC_TCPC_CTRL 0x19 +#define TCPC_TCPC_CTRL_ORIENTATION BIT(0) +#define PLUG_ORNT_CC1 0 +#define PLUG_ORNT_CC2 1 +#define TCPC_TCPC_CTRL_BIST_TM BIT(1) +#define TCPC_TCPC_CTRL_EN_LK4CONN_ALRT BIT(6) + +#define TCPC_EXTENDED_STATUS 0x20 +#define TCPC_EXTENDED_STATUS_VSAFE0V BIT(0) + +#define TCPC_ROLE_CTRL 0x1a +#define TCPC_ROLE_CTRL_DRP BIT(6) +#define TCPC_ROLE_CTRL_RP_VAL_SHIFT 4 +#define TCPC_ROLE_CTRL_RP_VAL_MASK 0x3 +#define TCPC_ROLE_CTRL_RP_VAL_DEF 0x0 +#define TCPC_ROLE_CTRL_RP_VAL_1_5 0x1 +#define TCPC_ROLE_CTRL_RP_VAL_3_0 0x2 +#define TCPC_ROLE_CTRL_CC2_SHIFT 2 +#define TCPC_ROLE_CTRL_CC2_MASK 0x3 +#define TCPC_ROLE_CTRL_CC1_SHIFT 0 +#define TCPC_ROLE_CTRL_CC1_MASK 0x3 +#define TCPC_ROLE_CTRL_CC_RA 0x0 +#define TCPC_ROLE_CTRL_CC_RP 0x1 +#define TCPC_ROLE_CTRL_CC_RD 0x2 +#define TCPC_ROLE_CTRL_CC_OPEN 0x3 + +#define TCPC_FAULT_CTRL 0x1b + +#define TCPC_POWER_CTRL 0x1c +#define TCPC_POWER_CTRL_VCONN_ENABLE BIT(0) +#define TCPC_POWER_CTRL_BLEED_DISCHARGE BIT(3) +#define TCPC_POWER_CTRL_AUTO_DISCHARGE BIT(4) +#define TCPC_DIS_VOLT_ALRM BIT(5) +#define TCPC_POWER_CTRL_VBUS_VOLT_MON BIT(6) +#define TCPC_FAST_ROLE_SWAP_EN BIT(7) + +#define TCPC_CC_STATUS 0x1d +#define TCPC_CC_STATUS_TOGGLING BIT(5) +#define TCPC_CC_STATUS_TERM BIT(4) +#define TCPC_CC_STATUS_TERM_RP 0 +#define TCPC_CC_STATUS_TERM_RD 1 +#define TCPC_CC_STATE_SRC_OPEN 0 +#define TCPC_CC_STATUS_CC2_SHIFT 2 +#define TCPC_CC_STATUS_CC2_MASK 0x3 +#define TCPC_CC_STATUS_CC1_SHIFT 0 +#define TCPC_CC_STATUS_CC1_MASK 0x3 + +#define TCPC_POWER_STATUS 0x1e +#define TCPC_POWER_STATUS_DBG_ACC_CON BIT(7) +#define TCPC_POWER_STATUS_UNINIT BIT(6) +#define TCPC_POWER_STATUS_SOURCING_VBUS BIT(4) +#define TCPC_POWER_STATUS_VBUS_DET BIT(3) +#define TCPC_POWER_STATUS_VBUS_PRES BIT(2) +#define TCPC_POWER_STATUS_VCONN_PRES BIT(1) +#define TCPC_POWER_STATUS_SINKING_VBUS BIT(0) + +#define TCPC_FAULT_STATUS 0x1f + +#define TCPC_ALERT_EXTENDED 0x21 + +#define TCPC_COMMAND 0x23 +#define TCPC_CMD_WAKE_I2C 0x11 +#define TCPC_CMD_DISABLE_VBUS_DETECT 0x22 +#define TCPC_CMD_ENABLE_VBUS_DETECT 0x33 +#define TCPC_CMD_DISABLE_SINK_VBUS 0x44 +#define TCPC_CMD_SINK_VBUS 0x55 +#define TCPC_CMD_DISABLE_SRC_VBUS 0x66 +#define TCPC_CMD_SRC_VBUS_DEFAULT 0x77 +#define TCPC_CMD_SRC_VBUS_HIGH 0x88 +#define TCPC_CMD_LOOK4CONNECTION 0x99 +#define TCPC_CMD_RXONEMORE 0xAA +#define TCPC_CMD_I2C_IDLE 0xFF + +#define TCPC_DEV_CAP_1 0x24 +#define TCPC_DEV_CAP_2 0x26 +#define TCPC_STD_INPUT_CAP 0x28 +#define TCPC_STD_OUTPUT_CAP 0x29 + +#define TCPC_MSG_HDR_INFO 0x2e +#define TCPC_MSG_HDR_INFO_DATA_ROLE BIT(3) +#define TCPC_MSG_HDR_INFO_PWR_ROLE BIT(0) +#define TCPC_MSG_HDR_INFO_REV_SHIFT 1 +#define TCPC_MSG_HDR_INFO_REV_MASK 0x3 + +#define TCPC_RX_DETECT 0x2f +#define TCPC_RX_DETECT_HARD_RESET BIT(5) +#define TCPC_RX_DETECT_SOP BIT(0) +#define TCPC_RX_DETECT_SOP1 BIT(1) +#define TCPC_RX_DETECT_SOP2 BIT(2) +#define TCPC_RX_DETECT_DBG1 BIT(3) +#define TCPC_RX_DETECT_DBG2 BIT(4) + +#define TCPC_RX_BYTE_CNT 0x30 +#define TCPC_RX_BUF_FRAME_TYPE 0x31 +#define TCPC_RX_BUF_FRAME_TYPE_SOP 0 +#define TCPC_RX_HDR 0x32 +#define TCPC_RX_DATA 0x34 /* through 0x4f */ + +#define TCPC_TRANSMIT 0x50 +#define TCPC_TRANSMIT_RETRY_SHIFT 4 +#define TCPC_TRANSMIT_RETRY_MASK 0x3 +#define TCPC_TRANSMIT_TYPE_SHIFT 0 +#define TCPC_TRANSMIT_TYPE_MASK 0x7 + +#define TCPC_TX_BYTE_CNT 0x51 +#define TCPC_TX_HDR 0x52 +#define TCPC_TX_DATA 0x54 /* through 0x6f */ + +#define TCPC_VBUS_VOLTAGE 0x70 +#define TCPC_VBUS_VOLTAGE_MASK 0x3ff +#define TCPC_VBUS_VOLTAGE_LSB_MV 25 +#define TCPC_VBUS_SINK_DISCONNECT_THRESH 0x72 +#define TCPC_VBUS_SINK_DISCONNECT_THRESH_LSB_MV 25 +#define TCPC_VBUS_SINK_DISCONNECT_THRESH_MAX 0x3ff +#define TCPC_VBUS_STOP_DISCHARGE_THRESH 0x74 +#define TCPC_VBUS_VOLTAGE_ALARM_HI_CFG 0x76 +#define TCPC_VBUS_VOLTAGE_ALARM_LO_CFG 0x78 + +/* I2C_WRITE_BYTE_COUNT + 1 when TX_BUF_BYTE_x is only accessible I2C_WRITE_BYTE_COUNT */ +#define TCPC_TRANSMIT_BUFFER_MAX_LEN 31 + +struct tcpci; + +/* + * @TX_BUF_BYTE_x_hidden: + * optional; Set when TX_BUF_BYTE_x can only be accessed through I2C_WRITE_BYTE_COUNT. + * @frs_sourcing_vbus: + * Optional; Callback to perform chip specific operations when FRS + * is sourcing vbus. + * @auto_discharge_disconnect: + * Optional; Enables TCPC to autonously discharge vbus on disconnect. + * @vbus_vsafe0v: + * optional; Set when TCPC can detect whether vbus is at VSAFE0V. + * @set_partner_usb_comm_capable: + * Optional; The USB Communications Capable bit indicates if port + * partner is capable of communication over the USB data lines + * (e.g. D+/- or SS Tx/Rx). Called to notify the status of the bit. + */ +struct tcpci_data { + struct regmap *regmap; + unsigned char TX_BUF_BYTE_x_hidden:1; + unsigned char auto_discharge_disconnect:1; + unsigned char vbus_vsafe0v:1; + + int (*init)(struct tcpci *tcpci, struct tcpci_data *data); + int (*set_vconn)(struct tcpci *tcpci, struct tcpci_data *data, + bool enable); + int (*start_drp_toggling)(struct tcpci *tcpci, struct tcpci_data *data, + enum typec_cc_status cc); + int (*set_vbus)(struct tcpci *tcpci, struct tcpci_data *data, bool source, bool sink); + void (*frs_sourcing_vbus)(struct tcpci *tcpci, struct tcpci_data *data); + void (*set_partner_usb_comm_capable)(struct tcpci *tcpci, struct tcpci_data *data, + bool capable); +}; + +struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data); +void tcpci_unregister_port(struct tcpci *tcpci); +irqreturn_t tcpci_irq(struct tcpci *tcpci); + +struct tcpm_port; +struct tcpm_port *tcpci_get_tcpm_port(struct tcpci *tcpci); +#endif /* __LINUX_USB_TCPCI_H */ -- cgit v1.2.3 From 620e8e8ba6210003fa45081b63d90ef4f3a0637f Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Thu, 30 Jun 2022 12:35:27 -0700 Subject: of/platform: Add stubs for of_platform_device_create/destroy() Code for platform_device_create() and of_platform_device_destroy() is only generated if CONFIG_OF_ADDRESS=y. Add stubs to avoid unresolved symbols when CONFIG_OF_ADDRESS is not set. Reviewed-by: Stephen Boyd Reviewed-by: Douglas Anderson Acked-by: Rob Herring Signed-off-by: Matthias Kaehlcke Link: https://lore.kernel.org/r/20220630123445.v24.1.I08fd2e1c775af04f663730e9fb4d00e6bbb38541@changeid Signed-off-by: Greg Kroah-Hartman --- include/linux/of_platform.h | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/of_platform.h b/include/linux/of_platform.h index 84a966623e78..d15b6cd5e1c3 100644 --- a/include/linux/of_platform.h +++ b/include/linux/of_platform.h @@ -61,16 +61,18 @@ static inline struct platform_device *of_find_device_by_node(struct device_node } #endif +extern int of_platform_bus_probe(struct device_node *root, + const struct of_device_id *matches, + struct device *parent); + +#ifdef CONFIG_OF_ADDRESS /* Platform devices and busses creation */ extern struct platform_device *of_platform_device_create(struct device_node *np, const char *bus_id, struct device *parent); extern int of_platform_device_destroy(struct device *dev, void *data); -extern int of_platform_bus_probe(struct device_node *root, - const struct of_device_id *matches, - struct device *parent); -#ifdef CONFIG_OF_ADDRESS + extern int of_platform_populate(struct device_node *root, const struct of_device_id *matches, const struct of_dev_auxdata *lookup, @@ -84,6 +86,18 @@ extern int devm_of_platform_populate(struct device *dev); extern void devm_of_platform_depopulate(struct device *dev); #else +/* Platform devices and busses creation */ +static inline struct platform_device *of_platform_device_create(struct device_node *np, + const char *bus_id, + struct device *parent) +{ + return NULL; +} +static inline int of_platform_device_destroy(struct device *dev, void *data) +{ + return -ENODEV; +} + static inline int of_platform_populate(struct device_node *root, const struct of_device_id *matches, const struct of_dev_auxdata *lookup, -- cgit v1.2.3 From 8bc063641cebf9d555e41d135db2b5035b521768 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Thu, 30 Jun 2022 12:35:29 -0700 Subject: usb: misc: Add onboard_usb_hub driver The main issue this driver addresses is that a USB hub needs to be powered before it can be discovered. For discrete onboard hubs (an example for such a hub is the Realtek RTS5411) this is often solved by supplying the hub with an 'always-on' regulator, which is kind of a hack. Some onboard hubs may require further initialization steps, like changing the state of a GPIO or enabling a clock, which requires even more hacks. This driver creates a platform device representing the hub which performs the necessary initialization. Currently it only supports switching on a single regulator, support for multiple regulators or other actions can be added as needed. Different initialization sequences can be supported based on the compatible string. Besides performing the initialization the driver can be configured to power the hub off during system suspend. This can help to extend battery life on battery powered devices which have no requirements to keep the hub powered during suspend. The driver can also be configured to leave the hub powered when a wakeup capable USB device is connected when suspending, and power it off otherwise. Technically the driver consists of two drivers, the platform driver described above and a very thin USB driver that subclasses the generic driver. The purpose of this driver is to provide the platform driver with the USB devices corresponding to the hub(s) (a hub controller may provide multiple 'logical' hubs, e.g. one to support USB 2.0 and another for USB 3.x). Co-developed-by: Ravi Chandra Sadineni Reviewed-by: Douglas Anderson Signed-off-by: Ravi Chandra Sadineni Signed-off-by: Matthias Kaehlcke Link: https://lore.kernel.org/r/20220630123445.v24.3.I7c9a1f1d6ced41dd8310e8a03da666a32364e790@changeid Signed-off-by: Greg Kroah-Hartman --- .../ABI/testing/sysfs-bus-platform-onboard-usb-hub | 8 + MAINTAINERS | 7 + drivers/usb/core/Makefile | 4 + drivers/usb/misc/Kconfig | 16 + drivers/usb/misc/Makefile | 1 + drivers/usb/misc/onboard_usb_hub.c | 428 +++++++++++++++++++++ drivers/usb/misc/onboard_usb_hub.h | 17 + drivers/usb/misc/onboard_usb_hub_pdevs.c | 142 +++++++ include/linux/usb/onboard_hub.h | 18 + 9 files changed, 641 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-platform-onboard-usb-hub create mode 100644 drivers/usb/misc/onboard_usb_hub.c create mode 100644 drivers/usb/misc/onboard_usb_hub.h create mode 100644 drivers/usb/misc/onboard_usb_hub_pdevs.c create mode 100644 include/linux/usb/onboard_hub.h (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-bus-platform-onboard-usb-hub b/Documentation/ABI/testing/sysfs-bus-platform-onboard-usb-hub new file mode 100644 index 000000000000..42deb0552065 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-platform-onboard-usb-hub @@ -0,0 +1,8 @@ +What: /sys/bus/platform/devices//always_powered_in_suspend +Date: June 2022 +KernelVersion: 5.20 +Contact: Matthias Kaehlcke + linux-usb@vger.kernel.org +Description: + (RW) Controls whether the USB hub remains always powered + during system suspend or not. \ No newline at end of file diff --git a/MAINTAINERS b/MAINTAINERS index 7533cb27adc0..f43b126eaf59 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14848,6 +14848,13 @@ S: Maintained T: git git://linuxtv.org/media_tree.git F: drivers/media/i2c/ov9734.c +ONBOARD USB HUB DRIVER +M: Matthias Kaehlcke +L: linux-usb@vger.kernel.org +S: Maintained +F: Documentation/ABI/testing/sysfs-bus-platform-onboard-usb-hub +F: drivers/usb/misc/onboard_usb_hub.c + ONENAND FLASH DRIVER M: Kyungmin Park L: linux-mtd@lists.infradead.org diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile index 18e874b0441e..7d338e9c0657 100644 --- a/drivers/usb/core/Makefile +++ b/drivers/usb/core/Makefile @@ -12,6 +12,10 @@ usbcore-$(CONFIG_OF) += of.o usbcore-$(CONFIG_USB_PCI) += hcd-pci.o usbcore-$(CONFIG_ACPI) += usb-acpi.o +ifdef CONFIG_USB_ONBOARD_HUB +usbcore-y += ../misc/onboard_usb_hub_pdevs.o +endif + obj-$(CONFIG_USB) += usbcore.o obj-$(CONFIG_USB_LEDS_TRIGGER_USBPORT) += ledtrig-usbport.o diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 4c5ddbd75b7e..9367c12c7e6f 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -295,3 +295,19 @@ config BRCM_USB_PINMAP This option enables support for remapping some USB external signals, which are typically on dedicated pins on the chip, to any gpio. + +config USB_ONBOARD_HUB + tristate "Onboard USB hub support" + depends on OF || COMPILE_TEST + help + Say Y here if you want to support discrete onboard USB hubs that + don't require an additional control bus for initialization, but + need some non-trivial form of initialization, such as enabling a + power regulator. An example for such a hub is the Realtek + RTS5411. + + This driver can be used as a module but its state (module vs + builtin) must match the state of the USB subsystem. Enabling + this config will enable the driver and it will automatically + match the state of the USB subsystem. If this driver is a + module it will be called onboard_usb_hub. diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index 35bdb4b6c3b6..93581baec3a8 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -33,3 +33,4 @@ obj-$(CONFIG_USB_CHAOSKEY) += chaoskey.o obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/ obj-$(CONFIG_USB_LINK_LAYER_TEST) += lvstest.o obj-$(CONFIG_BRCM_USB_PINMAP) += brcmstb-usb-pinmap.o +obj-$(CONFIG_USB_ONBOARD_HUB) += onboard_usb_hub.o diff --git a/drivers/usb/misc/onboard_usb_hub.c b/drivers/usb/misc/onboard_usb_hub.c new file mode 100644 index 000000000000..6b9b949d17d3 --- /dev/null +++ b/drivers/usb/misc/onboard_usb_hub.c @@ -0,0 +1,428 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for onboard USB hubs + * + * Copyright (c) 2022, Google LLC + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "onboard_usb_hub.h" + +static struct usb_device_driver onboard_hub_usbdev_driver; + +/************************** Platform driver **************************/ + +struct usbdev_node { + struct usb_device *udev; + struct list_head list; +}; + +struct onboard_hub { + struct regulator *vdd; + struct device *dev; + bool always_powered_in_suspend; + bool is_powered_on; + bool going_away; + struct list_head udev_list; + struct work_struct attach_usb_driver_work; + struct mutex lock; +}; + +static int onboard_hub_power_on(struct onboard_hub *hub) +{ + int err; + + err = regulator_enable(hub->vdd); + if (err) { + dev_err(hub->dev, "failed to enable regulator: %d\n", err); + return err; + } + + hub->is_powered_on = true; + + return 0; +} + +static int onboard_hub_power_off(struct onboard_hub *hub) +{ + int err; + + err = regulator_disable(hub->vdd); + if (err) { + dev_err(hub->dev, "failed to disable regulator: %d\n", err); + return err; + } + + hub->is_powered_on = false; + + return 0; +} + +static int __maybe_unused onboard_hub_suspend(struct device *dev) +{ + struct onboard_hub *hub = dev_get_drvdata(dev); + struct usbdev_node *node; + bool power_off = true; + + if (hub->always_powered_in_suspend) + return 0; + + mutex_lock(&hub->lock); + + list_for_each_entry(node, &hub->udev_list, list) { + if (!device_may_wakeup(node->udev->bus->controller)) + continue; + + if (usb_wakeup_enabled_descendants(node->udev)) { + power_off = false; + break; + } + } + + mutex_unlock(&hub->lock); + + if (!power_off) + return 0; + + return onboard_hub_power_off(hub); +} + +static int __maybe_unused onboard_hub_resume(struct device *dev) +{ + struct onboard_hub *hub = dev_get_drvdata(dev); + + if (hub->is_powered_on) + return 0; + + return onboard_hub_power_on(hub); +} + +static inline void get_udev_link_name(const struct usb_device *udev, char *buf, size_t size) +{ + snprintf(buf, size, "usb_dev.%s", dev_name(&udev->dev)); +} + +static int onboard_hub_add_usbdev(struct onboard_hub *hub, struct usb_device *udev) +{ + struct usbdev_node *node; + char link_name[64]; + int err; + + mutex_lock(&hub->lock); + + if (hub->going_away) { + err = -EINVAL; + goto error; + } + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) { + err = -ENOMEM; + goto error; + } + + node->udev = udev; + + list_add(&node->list, &hub->udev_list); + + mutex_unlock(&hub->lock); + + get_udev_link_name(udev, link_name, sizeof(link_name)); + WARN_ON(sysfs_create_link(&hub->dev->kobj, &udev->dev.kobj, link_name)); + + return 0; + +error: + mutex_unlock(&hub->lock); + + return err; +} + +static void onboard_hub_remove_usbdev(struct onboard_hub *hub, const struct usb_device *udev) +{ + struct usbdev_node *node; + char link_name[64]; + + get_udev_link_name(udev, link_name, sizeof(link_name)); + sysfs_remove_link(&hub->dev->kobj, link_name); + + mutex_lock(&hub->lock); + + list_for_each_entry(node, &hub->udev_list, list) { + if (node->udev == udev) { + list_del(&node->list); + kfree(node); + break; + } + } + + mutex_unlock(&hub->lock); +} + +static ssize_t always_powered_in_suspend_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + const struct onboard_hub *hub = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%d\n", hub->always_powered_in_suspend); +} + +static ssize_t always_powered_in_suspend_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct onboard_hub *hub = dev_get_drvdata(dev); + bool val; + int ret; + + ret = kstrtobool(buf, &val); + if (ret < 0) + return ret; + + hub->always_powered_in_suspend = val; + + return count; +} +static DEVICE_ATTR_RW(always_powered_in_suspend); + +static struct attribute *onboard_hub_attrs[] = { + &dev_attr_always_powered_in_suspend.attr, + NULL, +}; +ATTRIBUTE_GROUPS(onboard_hub); + +static void onboard_hub_attach_usb_driver(struct work_struct *work) +{ + int err; + + err = driver_attach(&onboard_hub_usbdev_driver.drvwrap.driver); + if (err) + pr_err("Failed to attach USB driver: %d\n", err); +} + +static int onboard_hub_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct onboard_hub *hub; + int err; + + hub = devm_kzalloc(dev, sizeof(*hub), GFP_KERNEL); + if (!hub) + return -ENOMEM; + + hub->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(hub->vdd)) + return PTR_ERR(hub->vdd); + + hub->dev = dev; + mutex_init(&hub->lock); + INIT_LIST_HEAD(&hub->udev_list); + + dev_set_drvdata(dev, hub); + + err = onboard_hub_power_on(hub); + if (err) + return err; + + /* + * The USB driver might have been detached from the USB devices by + * onboard_hub_remove() (e.g. through an 'unbind' by userspace), + * make sure to re-attach it if needed. + * + * This needs to be done deferred to avoid self-deadlocks on systems + * with nested onboard hubs. + */ + INIT_WORK(&hub->attach_usb_driver_work, onboard_hub_attach_usb_driver); + schedule_work(&hub->attach_usb_driver_work); + + return 0; +} + +static int onboard_hub_remove(struct platform_device *pdev) +{ + struct onboard_hub *hub = dev_get_drvdata(&pdev->dev); + struct usbdev_node *node; + struct usb_device *udev; + + hub->going_away = true; + + if (&hub->attach_usb_driver_work != current_work()) + cancel_work_sync(&hub->attach_usb_driver_work); + + mutex_lock(&hub->lock); + + /* unbind the USB devices to avoid dangling references to this device */ + while (!list_empty(&hub->udev_list)) { + node = list_first_entry(&hub->udev_list, struct usbdev_node, list); + udev = node->udev; + + /* + * Unbinding the driver will call onboard_hub_remove_usbdev(), + * which acquires hub->lock. We must release the lock first. + */ + get_device(&udev->dev); + mutex_unlock(&hub->lock); + device_release_driver(&udev->dev); + put_device(&udev->dev); + mutex_lock(&hub->lock); + } + + mutex_unlock(&hub->lock); + + return onboard_hub_power_off(hub); +} + +MODULE_DEVICE_TABLE(of, onboard_hub_match); + +static const struct dev_pm_ops __maybe_unused onboard_hub_pm_ops = { + SET_LATE_SYSTEM_SLEEP_PM_OPS(onboard_hub_suspend, onboard_hub_resume) +}; + +static struct platform_driver onboard_hub_driver = { + .probe = onboard_hub_probe, + .remove = onboard_hub_remove, + + .driver = { + .name = "onboard-usb-hub", + .of_match_table = onboard_hub_match, + .pm = pm_ptr(&onboard_hub_pm_ops), + .dev_groups = onboard_hub_groups, + }, +}; + +/************************** USB driver **************************/ + +#define VENDOR_ID_REALTEK 0x0bda + +/* + * Returns the onboard_hub platform device that is associated with the USB + * device passed as parameter. + */ +static struct onboard_hub *_find_onboard_hub(struct device *dev) +{ + struct platform_device *pdev; + struct device_node *np; + struct onboard_hub *hub; + + pdev = of_find_device_by_node(dev->of_node); + if (!pdev) { + np = of_parse_phandle(dev->of_node, "peer-hub", 0); + if (!np) { + dev_err(dev, "failed to find device node for peer hub\n"); + return ERR_PTR(-EINVAL); + } + + pdev = of_find_device_by_node(np); + of_node_put(np); + + if (!pdev) + return ERR_PTR(-ENODEV); + } + + hub = dev_get_drvdata(&pdev->dev); + put_device(&pdev->dev); + + /* + * The presence of drvdata ('hub') indicates that the platform driver + * finished probing. This handles the case where (conceivably) we could + * be running at the exact same time as the platform driver's probe. If + * we detect the race we request probe deferral and we'll come back and + * try again. + */ + if (!hub) + return ERR_PTR(-EPROBE_DEFER); + + return hub; +} + +static int onboard_hub_usbdev_probe(struct usb_device *udev) +{ + struct device *dev = &udev->dev; + struct onboard_hub *hub; + int err; + + /* ignore supported hubs without device tree node */ + if (!dev->of_node) + return -ENODEV; + + hub = _find_onboard_hub(dev); + if (IS_ERR(hub)) + return PTR_ERR(hub); + + dev_set_drvdata(dev, hub); + + err = onboard_hub_add_usbdev(hub, udev); + if (err) + return err; + + return 0; +} + +static void onboard_hub_usbdev_disconnect(struct usb_device *udev) +{ + struct onboard_hub *hub = dev_get_drvdata(&udev->dev); + + onboard_hub_remove_usbdev(hub, udev); +} + +static const struct usb_device_id onboard_hub_id_table[] = { + { USB_DEVICE(VENDOR_ID_REALTEK, 0x0411) }, /* RTS5411 USB 3.1 */ + { USB_DEVICE(VENDOR_ID_REALTEK, 0x5411) }, /* RTS5411 USB 2.1 */ + { USB_DEVICE(VENDOR_ID_REALTEK, 0x0414) }, /* RTS5414 USB 3.2 */ + { USB_DEVICE(VENDOR_ID_REALTEK, 0x5414) }, /* RTS5414 USB 2.1 */ + {} +}; +MODULE_DEVICE_TABLE(usb, onboard_hub_id_table); + +static struct usb_device_driver onboard_hub_usbdev_driver = { + .name = "onboard-usb-hub", + .probe = onboard_hub_usbdev_probe, + .disconnect = onboard_hub_usbdev_disconnect, + .generic_subclass = 1, + .supports_autosuspend = 1, + .id_table = onboard_hub_id_table, +}; + +static int __init onboard_hub_init(void) +{ + int ret; + + ret = platform_driver_register(&onboard_hub_driver); + if (ret) + return ret; + + ret = usb_register_device_driver(&onboard_hub_usbdev_driver, THIS_MODULE); + if (ret) + platform_driver_unregister(&onboard_hub_driver); + + return ret; +} +module_init(onboard_hub_init); + +static void __exit onboard_hub_exit(void) +{ + usb_deregister_device_driver(&onboard_hub_usbdev_driver); + platform_driver_unregister(&onboard_hub_driver); +} +module_exit(onboard_hub_exit); + +MODULE_AUTHOR("Matthias Kaehlcke "); +MODULE_DESCRIPTION("Driver for discrete onboard USB hubs"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/misc/onboard_usb_hub.h b/drivers/usb/misc/onboard_usb_hub.h new file mode 100644 index 000000000000..d3a5b6938582 --- /dev/null +++ b/drivers/usb/misc/onboard_usb_hub.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2022, Google LLC + */ + +#ifndef _USB_MISC_ONBOARD_USB_HUB_H +#define _USB_MISC_ONBOARD_USB_HUB_H + +static const struct of_device_id onboard_hub_match[] = { + { .compatible = "usbbda,411" }, + { .compatible = "usbbda,5411" }, + { .compatible = "usbbda,414" }, + { .compatible = "usbbda,5414" }, + {} +}; + +#endif /* _USB_MISC_ONBOARD_USB_HUB_H */ diff --git a/drivers/usb/misc/onboard_usb_hub_pdevs.c b/drivers/usb/misc/onboard_usb_hub_pdevs.c new file mode 100644 index 000000000000..a0a5f719129f --- /dev/null +++ b/drivers/usb/misc/onboard_usb_hub_pdevs.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * API for creating and destroying USB onboard hub platform devices + * + * Copyright (c) 2022, Google LLC + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "onboard_usb_hub.h" + +struct pdev_list_entry { + struct platform_device *pdev; + struct list_head node; +}; + +static bool of_is_onboard_usb_hub(const struct device_node *np) +{ + return !!of_match_node(onboard_hub_match, np); +} + +/** + * onboard_hub_create_pdevs -- create platform devices for onboard USB hubs + * @parent_hub : parent hub to scan for connected onboard hubs + * @pdev_list : list of onboard hub platform devices owned by the parent hub + * + * Creates a platform device for each supported onboard hub that is connected to + * the given parent hub. The platform device is in charge of initializing the + * hub (enable regulators, take the hub out of reset, ...) and can optionally + * control whether the hub remains powered during system suspend or not. + * + * To keep track of the platform devices they are added to a list that is owned + * by the parent hub. + * + * Some background about the logic in this function, which can be a bit hard + * to follow: + * + * Root hubs don't have dedicated device tree nodes, but use the node of their + * HCD. The primary and secondary HCD are usually represented by a single DT + * node. That means the root hubs of the primary and secondary HCD share the + * same device tree node (the HCD node). As a result this function can be called + * twice with the same DT node for root hubs. We only want to create a single + * platform device for each physical onboard hub, hence for root hubs the loop + * is only executed for the root hub of the primary HCD. Since the function + * scans through all child nodes it still creates pdevs for onboard hubs + * connected to the root hub of the secondary HCD if needed. + * + * Further there must be only one platform device for onboard hubs with a peer + * hub (the hub is a single physical device). To achieve this two measures are + * taken: pdevs for onboard hubs with a peer are only created when the function + * is called on behalf of the parent hub that is connected to the primary HCD + * (directly or through other hubs). For onboard hubs connected to root hubs + * the function processes the nodes of both peers. A platform device is only + * created if the peer hub doesn't have one already. + */ +void onboard_hub_create_pdevs(struct usb_device *parent_hub, struct list_head *pdev_list) +{ + int i; + struct usb_hcd *hcd = bus_to_hcd(parent_hub->bus); + struct device_node *np, *npc; + struct platform_device *pdev; + struct pdev_list_entry *pdle; + + if (!parent_hub->dev.of_node) + return; + + if (!parent_hub->parent && !usb_hcd_is_primary_hcd(hcd)) + return; + + for (i = 1; i <= parent_hub->maxchild; i++) { + np = usb_of_get_device_node(parent_hub, i); + if (!np) + continue; + + if (!of_is_onboard_usb_hub(np)) + goto node_put; + + npc = of_parse_phandle(np, "peer-hub", 0); + if (npc) { + if (!usb_hcd_is_primary_hcd(hcd)) { + of_node_put(npc); + goto node_put; + } + + pdev = of_find_device_by_node(npc); + of_node_put(npc); + + if (pdev) { + put_device(&pdev->dev); + goto node_put; + } + } + + pdev = of_platform_device_create(np, NULL, &parent_hub->dev); + if (!pdev) { + dev_err(&parent_hub->dev, + "failed to create platform device for onboard hub '%pOF'\n", np); + goto node_put; + } + + pdle = kzalloc(sizeof(*pdle), GFP_KERNEL); + if (!pdle) { + of_platform_device_destroy(&pdev->dev, NULL); + goto node_put; + } + + pdle->pdev = pdev; + list_add(&pdle->node, pdev_list); + +node_put: + of_node_put(np); + } +} +EXPORT_SYMBOL_GPL(onboard_hub_create_pdevs); + +/** + * onboard_hub_destroy_pdevs -- free resources of onboard hub platform devices + * @pdev_list : list of onboard hub platform devices + * + * Destroys the platform devices in the given list and frees the memory associated + * with the list entry. + */ +void onboard_hub_destroy_pdevs(struct list_head *pdev_list) +{ + struct pdev_list_entry *pdle, *tmp; + + list_for_each_entry_safe(pdle, tmp, pdev_list, node) { + list_del(&pdle->node); + of_platform_device_destroy(&pdle->pdev->dev, NULL); + kfree(pdle); + } +} +EXPORT_SYMBOL_GPL(onboard_hub_destroy_pdevs); diff --git a/include/linux/usb/onboard_hub.h b/include/linux/usb/onboard_hub.h new file mode 100644 index 000000000000..d9373230556e --- /dev/null +++ b/include/linux/usb/onboard_hub.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __LINUX_USB_ONBOARD_HUB_H +#define __LINUX_USB_ONBOARD_HUB_H + +struct usb_device; +struct list_head; + +#if IS_ENABLED(CONFIG_USB_ONBOARD_HUB) +void onboard_hub_create_pdevs(struct usb_device *parent_hub, struct list_head *pdev_list); +void onboard_hub_destroy_pdevs(struct list_head *pdev_list); +#else +static inline void onboard_hub_create_pdevs(struct usb_device *parent_hub, + struct list_head *pdev_list) {} +static inline void onboard_hub_destroy_pdevs(struct list_head *pdev_list) {} +#endif + +#endif /* __LINUX_USB_ONBOARD_HUB_H */ -- cgit v1.2.3 From ddaf8d96f93bccb3f2b1f4f156c098b272440004 Mon Sep 17 00:00:00 2001 From: Prashant Malani Date: Mon, 11 Jul 2022 07:22:55 +0000 Subject: usb: typec: Add support for retimers Introduce a retimer device class and associated functions that register and use retimer "switch" devices. These operate in a manner similar to the "mode-switch" and help configure retimers that exist between the Type-C connector and host controller(s). Type C ports can be linked to retimers using firmware node device references (again, in a manner similar to "mode-switch"). There are no new sysfs files being created; there is the new retimer class directory, but there are no class-specific files being created there. Signed-off-by: Prashant Malani Acked-by: Heikki Krogerus Link: https://lore.kernel.org/r/20220711072333.2064341-2-pmalani@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/Makefile | 2 +- drivers/usb/typec/class.c | 9 +- drivers/usb/typec/class.h | 1 + drivers/usb/typec/retimer.c | 168 ++++++++++++++++++++++++++++++++++++++ drivers/usb/typec/retimer.h | 15 ++++ include/linux/usb/typec_retimer.h | 45 ++++++++++ 6 files changed, 238 insertions(+), 2 deletions(-) create mode 100644 drivers/usb/typec/retimer.c create mode 100644 drivers/usb/typec/retimer.h create mode 100644 include/linux/usb/typec_retimer.h (limited to 'include') diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile index dac5c11a3234..4a83dad51a6c 100644 --- a/drivers/usb/typec/Makefile +++ b/drivers/usb/typec/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_TYPEC) += typec.o -typec-y := class.o mux.o bus.o pd.o +typec-y := class.o mux.o bus.o pd.o retimer.o typec-$(CONFIG_ACPI) += port-mapper.o obj-$(CONFIG_TYPEC) += altmodes/ obj-$(CONFIG_TYPEC_TCPM) += tcpm/ diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index bbc46b14f99a..9062836bb638 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -2299,10 +2299,14 @@ static int __init typec_init(void) if (ret) goto err_unregister_bus; - ret = class_register(&typec_class); + ret = class_register(&retimer_class); if (ret) goto err_unregister_mux_class; + ret = class_register(&typec_class); + if (ret) + goto err_unregister_retimer_class; + ret = usb_power_delivery_init(); if (ret) goto err_unregister_class; @@ -2312,6 +2316,9 @@ static int __init typec_init(void) err_unregister_class: class_unregister(&typec_class); +err_unregister_retimer_class: + class_unregister(&retimer_class); + err_unregister_mux_class: class_unregister(&typec_mux_class); diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h index b531f9853bc0..43fcf9e37a8c 100644 --- a/drivers/usb/typec/class.h +++ b/drivers/usb/typec/class.h @@ -76,6 +76,7 @@ extern const struct device_type typec_port_dev_type; #define is_typec_port(dev) ((dev)->type == &typec_port_dev_type) extern struct class typec_mux_class; +extern struct class retimer_class; extern struct class typec_class; #if defined(CONFIG_ACPI) diff --git a/drivers/usb/typec/retimer.c b/drivers/usb/typec/retimer.c new file mode 100644 index 000000000000..051eaa7d2899 --- /dev/null +++ b/drivers/usb/typec/retimer.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2022 Google LLC + * + * USB Type-C Retimer support. + * Author: Prashant Malani + * + */ + +#include +#include +#include +#include +#include +#include + +#include "class.h" +#include "retimer.h" + +static bool dev_name_ends_with(struct device *dev, const char *suffix) +{ + const char *name = dev_name(dev); + const int name_len = strlen(name); + const int suffix_len = strlen(suffix); + + if (suffix_len > name_len) + return false; + + return strcmp(name + (name_len - suffix_len), suffix) == 0; +} + +static int retimer_fwnode_match(struct device *dev, const void *fwnode) +{ + return dev_fwnode(dev) == fwnode && dev_name_ends_with(dev, "-retimer"); +} + +static void *typec_retimer_match(struct fwnode_handle *fwnode, const char *id, void *data) +{ + struct device *dev = class_find_device(&retimer_class, NULL, fwnode, + retimer_fwnode_match); + + return dev ? to_typec_retimer(dev) : ERR_PTR(-EPROBE_DEFER); +} + +/** + * fwnode_typec_retimer_get - Find USB Type-C retimer. + * @fwnode: The caller device node. + * + * Finds a retimer linked to the caller. This function is primarily meant for the + * Type-C drivers. Returns a reference to the retimer on success, NULL if no + * matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a connection + * was found but the retimer has not been enumerated yet. + */ +struct typec_retimer *fwnode_typec_retimer_get(struct fwnode_handle *fwnode) +{ + struct typec_retimer *retimer; + + retimer = fwnode_connection_find_match(fwnode, "retimer-switch", NULL, typec_retimer_match); + if (!IS_ERR_OR_NULL(retimer)) + WARN_ON(!try_module_get(retimer->dev.parent->driver->owner)); + + return retimer; +} +EXPORT_SYMBOL_GPL(fwnode_typec_retimer_get); + +/** + * typec_retimer_put - Release handle to a retimer. + * @retimer: USB Type-C Connector Retimer. + * + * Decrements reference count for @retimer. + */ +void typec_retimer_put(struct typec_retimer *retimer) +{ + if (!IS_ERR_OR_NULL(retimer)) { + module_put(retimer->dev.parent->driver->owner); + put_device(&retimer->dev); + } +} +EXPORT_SYMBOL_GPL(typec_retimer_put); + +int typec_retimer_set(struct typec_retimer *retimer, struct typec_retimer_state *state) +{ + if (IS_ERR_OR_NULL(retimer)) + return 0; + + return retimer->set(retimer, state); +} +EXPORT_SYMBOL_GPL(typec_retimer_set); + +static void typec_retimer_release(struct device *dev) +{ + kfree(to_typec_retimer(dev)); +} + +static const struct device_type typec_retimer_dev_type = { + .name = "typec_retimer", + .release = typec_retimer_release, +}; + +/** + * typec_retimer_register - Register a retimer device. + * @parent: Parent device. + * @desc: Retimer description. + * + * Some USB Type-C connectors have their physical lines routed through retimers before they + * reach muxes or host controllers. In some cases (for example: using alternate modes) + * these retimers need to be reconfigured appropriately. This function registers retimer + * switches which route and potentially modify the signals on the Type C physical lines + * enroute to the host controllers. + */ +struct typec_retimer * +typec_retimer_register(struct device *parent, const struct typec_retimer_desc *desc) +{ + struct typec_retimer *retimer; + int ret; + + if (!desc || !desc->set) + return ERR_PTR(-EINVAL); + + retimer = kzalloc(sizeof(*retimer), GFP_KERNEL); + if (!retimer) + return ERR_PTR(-ENOMEM); + + retimer->set = desc->set; + + device_initialize(&retimer->dev); + retimer->dev.parent = parent; + retimer->dev.fwnode = desc->fwnode; + retimer->dev.class = &retimer_class; + retimer->dev.type = &typec_retimer_dev_type; + retimer->dev.driver_data = desc->drvdata; + dev_set_name(&retimer->dev, "%s-retimer", + desc->name ? desc->name : dev_name(parent)); + + ret = device_add(&retimer->dev); + if (ret) { + dev_err(parent, "failed to register retimer (%d)\n", ret); + put_device(&retimer->dev); + return ERR_PTR(ret); + } + + return retimer; +} +EXPORT_SYMBOL_GPL(typec_retimer_register); + +/** + * typec_retimer_unregister - Unregister retimer device. + * @retimer: USB Type-C Connector retimer. + * + * Unregister retimer that was registered with typec_retimer_register(). + */ +void typec_retimer_unregister(struct typec_retimer *retimer) +{ + if (!IS_ERR_OR_NULL(retimer)) + device_unregister(&retimer->dev); +} +EXPORT_SYMBOL_GPL(typec_retimer_unregister); + +void *typec_retimer_get_drvdata(struct typec_retimer *retimer) +{ + return dev_get_drvdata(&retimer->dev); +} +EXPORT_SYMBOL_GPL(typec_retimer_get_drvdata); + +struct class retimer_class = { + .name = "retimer", + .owner = THIS_MODULE, +}; diff --git a/drivers/usb/typec/retimer.h b/drivers/usb/typec/retimer.h new file mode 100644 index 000000000000..fa15951d4846 --- /dev/null +++ b/drivers/usb/typec/retimer.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __USB_TYPEC_RETIMER__ +#define __USB_TYPEC_RETIMER__ + +#include + +struct typec_retimer { + struct device dev; + typec_retimer_set_fn_t set; +}; + +#define to_typec_retimer(_dev_) container_of(_dev_, struct typec_retimer, dev) + +#endif /* __USB_TYPEC_RETIMER__ */ diff --git a/include/linux/usb/typec_retimer.h b/include/linux/usb/typec_retimer.h new file mode 100644 index 000000000000..5e036b3360e2 --- /dev/null +++ b/include/linux/usb/typec_retimer.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __USB_TYPEC_RETIMER +#define __USB_TYPEC_RETIMER + +#include +#include + +struct device; +struct typec_retimer; +struct typec_altmode; +struct fwnode_handle; + +struct typec_retimer_state { + struct typec_altmode *alt; + unsigned long mode; + void *data; +}; + +typedef int (*typec_retimer_set_fn_t)(struct typec_retimer *retimer, + struct typec_retimer_state *state); + +struct typec_retimer_desc { + struct fwnode_handle *fwnode; + typec_retimer_set_fn_t set; + const char *name; + void *drvdata; +}; + +struct typec_retimer *fwnode_typec_retimer_get(struct fwnode_handle *fwnode); +void typec_retimer_put(struct typec_retimer *retimer); +int typec_retimer_set(struct typec_retimer *retimer, struct typec_retimer_state *state); + +static inline struct typec_retimer *typec_retimer_get(struct device *dev) +{ + return fwnode_typec_retimer_get(dev_fwnode(dev)); +} + +struct typec_retimer * +typec_retimer_register(struct device *parent, const struct typec_retimer_desc *desc); +void typec_retimer_unregister(struct typec_retimer *retimer); + +void *typec_retimer_get_drvdata(struct typec_retimer *retimer); + +#endif /* __USB_TYPEC_RETIMER */ -- cgit v1.2.3 From 28a6ed8e39f77f6ac613ec9b7461aa75e85fa79a Mon Sep 17 00:00:00 2001 From: Prashant Malani Date: Mon, 11 Jul 2022 07:22:57 +0000 Subject: platform/chrome: Add Type-C mux set command definitions Copy EC header definitions for the USB Type-C Mux control command from the EC code base. Also pull in "TBT_UFP_REPLY" definitions, since that is the prior entry in the enum. These headers are already present in the EC code base. [1] [1] https://chromium.googlesource.com/chromiumos/platform/ec/+/b80f85a94a423273c1638ef7b662c56931a138dd/include/ec_commands.h Signed-off-by: Prashant Malani Link: https://lore.kernel.org/r/20220711072333.2064341-4-pmalani@chromium.org Signed-off-by: Greg Kroah-Hartman --- include/linux/platform_data/cros_ec_commands.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'include') diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index 8cfa8cfca77e..a3945c5e7f50 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -5722,8 +5722,21 @@ enum typec_control_command { TYPEC_CONTROL_COMMAND_EXIT_MODES, TYPEC_CONTROL_COMMAND_CLEAR_EVENTS, TYPEC_CONTROL_COMMAND_ENTER_MODE, + TYPEC_CONTROL_COMMAND_TBT_UFP_REPLY, + TYPEC_CONTROL_COMMAND_USB_MUX_SET, }; +/* Replies the AP may specify to the TBT EnterMode command as a UFP */ +enum typec_tbt_ufp_reply { + TYPEC_TBT_UFP_REPLY_NAK, + TYPEC_TBT_UFP_REPLY_ACK, +}; + +struct typec_usb_mux_set { + uint8_t mux_index; /* Index of the mux to set in the chain */ + uint8_t mux_flags; /* USB_PD_MUX_*-encoded USB mux state to set */ +} __ec_align1; + struct ec_params_typec_control { uint8_t port; uint8_t command; /* enum typec_control_command */ @@ -5737,6 +5750,8 @@ struct ec_params_typec_control { union { uint32_t clear_events_mask; uint8_t mode_to_enter; /* enum typec_mode */ + uint8_t tbt_ufp_reply; /* enum typec_tbt_ufp_reply */ + struct typec_usb_mux_set mux_params; uint8_t placeholder[128]; }; } __ec_align1; @@ -5815,6 +5830,9 @@ enum tcpc_cc_polarity { #define PD_STATUS_EVENT_SOP_DISC_DONE BIT(0) #define PD_STATUS_EVENT_SOP_PRIME_DISC_DONE BIT(1) #define PD_STATUS_EVENT_HARD_RESET BIT(2) +#define PD_STATUS_EVENT_DISCONNECTED BIT(3) +#define PD_STATUS_EVENT_MUX_0_SET_DONE BIT(4) +#define PD_STATUS_EVENT_MUX_1_SET_DONE BIT(5) struct ec_params_typec_status { uint8_t port; -- cgit v1.2.3 From 32f02a211b0a192553075803b0b819027f96bb43 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 19 Jul 2022 13:57:48 +0200 Subject: Revert "platform/chrome: Add Type-C mux set command definitions" This reverts commit 28a6ed8e39f77f6ac613ec9b7461aa75e85fa79a. The chrome platform driver changes need to come in through the platform tree due to some api changes that showed up there that cause build errors in linux-next Reported-by: Stephen Rothwell Link: https://lore.kernel.org/r/20220719160821.5e68e30b@oak.ozlabs.ibm.com Cc: Prashant Malani Signed-off-by: Greg Kroah-Hartman --- include/linux/platform_data/cros_ec_commands.h | 18 ------------------ 1 file changed, 18 deletions(-) (limited to 'include') diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index a3945c5e7f50..8cfa8cfca77e 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -5722,21 +5722,8 @@ enum typec_control_command { TYPEC_CONTROL_COMMAND_EXIT_MODES, TYPEC_CONTROL_COMMAND_CLEAR_EVENTS, TYPEC_CONTROL_COMMAND_ENTER_MODE, - TYPEC_CONTROL_COMMAND_TBT_UFP_REPLY, - TYPEC_CONTROL_COMMAND_USB_MUX_SET, }; -/* Replies the AP may specify to the TBT EnterMode command as a UFP */ -enum typec_tbt_ufp_reply { - TYPEC_TBT_UFP_REPLY_NAK, - TYPEC_TBT_UFP_REPLY_ACK, -}; - -struct typec_usb_mux_set { - uint8_t mux_index; /* Index of the mux to set in the chain */ - uint8_t mux_flags; /* USB_PD_MUX_*-encoded USB mux state to set */ -} __ec_align1; - struct ec_params_typec_control { uint8_t port; uint8_t command; /* enum typec_control_command */ @@ -5750,8 +5737,6 @@ struct ec_params_typec_control { union { uint32_t clear_events_mask; uint8_t mode_to_enter; /* enum typec_mode */ - uint8_t tbt_ufp_reply; /* enum typec_tbt_ufp_reply */ - struct typec_usb_mux_set mux_params; uint8_t placeholder[128]; }; } __ec_align1; @@ -5830,9 +5815,6 @@ enum tcpc_cc_polarity { #define PD_STATUS_EVENT_SOP_DISC_DONE BIT(0) #define PD_STATUS_EVENT_SOP_PRIME_DISC_DONE BIT(1) #define PD_STATUS_EVENT_HARD_RESET BIT(2) -#define PD_STATUS_EVENT_DISCONNECTED BIT(3) -#define PD_STATUS_EVENT_MUX_0_SET_DONE BIT(4) -#define PD_STATUS_EVENT_MUX_1_SET_DONE BIT(5) struct ec_params_typec_status { uint8_t port; -- cgit v1.2.3 From b4023554b1fb49f73a09e5f346a5facbf27d7383 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 25 Jul 2022 09:58:35 +0200 Subject: USB: cdc: add control-signal defines Add defines for the Control Signal Bitmap Values from section 6.2.14 SetControlLineState of the CDC specification version 1.1. Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20220725075841.1187-2-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/usb/cdc.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/usb/cdc.h b/include/uapi/linux/usb/cdc.h index 6d61550959ef..372c81425cae 100644 --- a/include/uapi/linux/usb/cdc.h +++ b/include/uapi/linux/usb/cdc.h @@ -271,6 +271,10 @@ struct usb_cdc_line_coding { __u8 bDataBits; } __attribute__ ((packed)); +/* Control Signal Bitmap Values from 6.2.14 SetControlLineState */ +#define USB_CDC_CTRL_DTR (1 << 0) +#define USB_CDC_CTRL_RTS (1 << 1) + /* table 62; bits in multicast filter */ #define USB_CDC_PACKET_TYPE_PROMISCUOUS (1 << 0) #define USB_CDC_PACKET_TYPE_ALL_MULTICAST (1 << 1) /* no filter */ -- cgit v1.2.3 From a0a3202b44a9fdf2a1f6330a0d176aee76c8631d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 25 Jul 2022 09:58:36 +0200 Subject: USB: cdc: add serial-state defines Add defines for the serial-state bitmap values from section 6.3.5 SerialState of the CDC specification version 1.1. Note that the bTxCarrier and bRxCarrier bits have been named after their RS-232 signal equivalents DSR and DCD. Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20220725075841.1187-3-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/usb/cdc.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/usb/cdc.h b/include/uapi/linux/usb/cdc.h index 372c81425cae..78caa9bdc4ae 100644 --- a/include/uapi/linux/usb/cdc.h +++ b/include/uapi/linux/usb/cdc.h @@ -306,6 +306,15 @@ struct usb_cdc_notification { __le16 wLength; } __attribute__ ((packed)); +/* UART State Bitmap Values from 6.3.5 SerialState */ +#define USB_CDC_SERIAL_STATE_DCD (1 << 0) +#define USB_CDC_SERIAL_STATE_DSR (1 << 1) +#define USB_CDC_SERIAL_STATE_BREAK (1 << 2) +#define USB_CDC_SERIAL_STATE_RING_SIGNAL (1 << 3) +#define USB_CDC_SERIAL_STATE_FRAMING (1 << 4) +#define USB_CDC_SERIAL_STATE_PARITY (1 << 5) +#define USB_CDC_SERIAL_STATE_OVERRUN (1 << 6) + struct usb_cdc_speed_change { __le32 DLBitRRate; /* contains the downlink bit rate (IN pipe) */ __le32 ULBitRate; /* contains the uplink bit rate (OUT pipe) */ -- cgit v1.2.3 From 26c6c2f8a907c9e3a2f24990552a4d77235791e6 Mon Sep 17 00:00:00 2001 From: Weitao Wang Date: Tue, 26 Jul 2022 15:49:18 +0800 Subject: USB: HCD: Fix URB giveback issue in tasklet function Usb core introduce the mechanism of giveback of URB in tasklet context to reduce hardware interrupt handling time. On some test situation(such as FIO with 4KB block size), when tasklet callback function called to giveback URB, interrupt handler add URB node to the bh->head list also. If check bh->head list again after finish all URB giveback of local_list, then it may introduce a "dynamic balance" between giveback URB and add URB to bh->head list. This tasklet callback function may not exit for a long time, which will cause other tasklet function calls to be delayed. Some real-time applications(such as KB and Mouse) will see noticeable lag. In order to prevent the tasklet function from occupying the cpu for a long time at a time, new URBS will not be added to the local_list even though the bh->head list is not empty. But also need to ensure the left URB giveback to be processed in time, so add a member high_prio for structure giveback_urb_bh to prioritize tasklet and schelule this tasklet again if bh->head list is not empty. At the same time, we are able to prioritize tasklet through structure member high_prio. So, replace the local high_prio_bh variable with this structure member in usb_hcd_giveback_urb. Fixes: 94dfd7edfd5c ("USB: HCD: support giveback of URB in tasklet context") Cc: stable Reviewed-by: Alan Stern Signed-off-by: Weitao Wang Link: https://lore.kernel.org/r/20220726074918.5114-1-WeitaoWang-oc@zhaoxin.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 26 +++++++++++++++----------- include/linux/usb/hcd.h | 1 + 2 files changed, 16 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 41dcd41e550c..a6a87c5d1b05 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1691,7 +1691,6 @@ static void usb_giveback_urb_bh(struct tasklet_struct *t) spin_lock_irq(&bh->lock); bh->running = true; - restart: list_replace_init(&bh->head, &local_list); spin_unlock_irq(&bh->lock); @@ -1705,10 +1704,17 @@ static void usb_giveback_urb_bh(struct tasklet_struct *t) bh->completing_ep = NULL; } - /* check if there are new URBs to giveback */ + /* + * giveback new URBs next time to prevent this function + * from not exiting for a long time. + */ spin_lock_irq(&bh->lock); - if (!list_empty(&bh->head)) - goto restart; + if (!list_empty(&bh->head)) { + if (bh->high_prio) + tasklet_hi_schedule(&bh->bh); + else + tasklet_schedule(&bh->bh); + } bh->running = false; spin_unlock_irq(&bh->lock); } @@ -1737,7 +1743,7 @@ static void usb_giveback_urb_bh(struct tasklet_struct *t) void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status) { struct giveback_urb_bh *bh; - bool running, high_prio_bh; + bool running; /* pass status to tasklet via unlinked */ if (likely(!urb->unlinked)) @@ -1748,13 +1754,10 @@ void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status) return; } - if (usb_pipeisoc(urb->pipe) || usb_pipeint(urb->pipe)) { + if (usb_pipeisoc(urb->pipe) || usb_pipeint(urb->pipe)) bh = &hcd->high_prio_bh; - high_prio_bh = true; - } else { + else bh = &hcd->low_prio_bh; - high_prio_bh = false; - } spin_lock(&bh->lock); list_add_tail(&urb->urb_list, &bh->head); @@ -1763,7 +1766,7 @@ void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status) if (running) ; - else if (high_prio_bh) + else if (bh->high_prio) tasklet_hi_schedule(&bh->bh); else tasklet_schedule(&bh->bh); @@ -2959,6 +2962,7 @@ int usb_add_hcd(struct usb_hcd *hcd, /* initialize tasklets */ init_giveback_urb_bh(&hcd->high_prio_bh); + hcd->high_prio_bh.high_prio = true; init_giveback_urb_bh(&hcd->low_prio_bh); /* enable irqs just before we start the controller, diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 2c1fc9212cf2..98d1921f02b1 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -66,6 +66,7 @@ struct giveback_urb_bh { bool running; + bool high_prio; spinlock_t lock; struct list_head head; struct tasklet_struct bh; -- cgit v1.2.3