From 5ed132db5ad4f58156ae9d28219396b6f764a9cb Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Tue, 20 Oct 2020 02:36:18 -0700 Subject: dt-bindings: connector: Add property to set initial current cap for FRS This change adds frs-typec-current which allows setting the initial current capability of the new source when vSafe5V is applied during PD3.0 sink Fast Role Swap. Signed-off-by: Badhri Jagan Sridharan Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20201020093627.256885-2-badhri@google.com Signed-off-by: Greg Kroah-Hartman --- include/dt-bindings/usb/pd.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include') diff --git a/include/dt-bindings/usb/pd.h b/include/dt-bindings/usb/pd.h index 985f2bbd4d24..0352893697f0 100644 --- a/include/dt-bindings/usb/pd.h +++ b/include/dt-bindings/usb/pd.h @@ -85,4 +85,12 @@ PDO_PPS_APDO_MIN_VOLT(min_mv) | PDO_PPS_APDO_MAX_VOLT(max_mv) | \ PDO_PPS_APDO_MAX_CURR(max_ma)) + /* + * Based on "Table 6-14 Fixed Supply PDO - Sink" of "USB Power Delivery Specification Revision 3.0, + * Version 1.2" + * Initial current capability of the new source when vSafe5V is applied. + */ +#define FRS_DEFAULT_POWER 1 +#define FRS_5V_1P5A 2 +#define FRS_5V_3A 3 #endif /* __DT_POWER_DELIVERY_H */ -- cgit v1.2.3 From 179dfb954790410f65605f1c479c029c2cd73c55 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 26 Oct 2020 11:44:21 +0100 Subject: USB: serial: remove write wait queue The digi_acceleport driver is the only driver still using the port write wake queue so move it to that driver's port data. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/digi_acceleport.c | 12 ++++++------ include/linux/usb/serial.h | 2 -- 2 files changed, 6 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 968e08cdcb09..0ecd5316d85f 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -197,6 +197,7 @@ struct digi_port { int dp_throttle_restart; wait_queue_head_t dp_flush_wait; wait_queue_head_t dp_close_wait; /* wait queue for close */ + wait_queue_head_t write_wait; struct usb_serial_port *dp_port; }; @@ -381,7 +382,7 @@ static int digi_write_oob_command(struct usb_serial_port *port, while (count > 0) { while (oob_priv->dp_write_urb_in_use) { cond_wait_interruptible_timeout_irqrestore( - &oob_port->write_wait, DIGI_RETRY_TIMEOUT, + &oob_priv->write_wait, DIGI_RETRY_TIMEOUT, &oob_priv->dp_port_lock, flags); if (interruptible && signal_pending(current)) return -EINTR; @@ -444,7 +445,7 @@ static int digi_write_inb_command(struct usb_serial_port *port, while (priv->dp_write_urb_in_use && time_before(jiffies, timeout)) { cond_wait_interruptible_timeout_irqrestore( - &port->write_wait, DIGI_RETRY_TIMEOUT, + &priv->write_wait, DIGI_RETRY_TIMEOUT, &priv->dp_port_lock, flags); if (signal_pending(current)) return -EINTR; @@ -523,7 +524,7 @@ static int digi_set_modem_signals(struct usb_serial_port *port, while (oob_priv->dp_write_urb_in_use) { spin_unlock(&port_priv->dp_port_lock); cond_wait_interruptible_timeout_irqrestore( - &oob_port->write_wait, DIGI_RETRY_TIMEOUT, + &oob_priv->write_wait, DIGI_RETRY_TIMEOUT, &oob_priv->dp_port_lock, flags); if (interruptible && signal_pending(current)) return -EINTR; @@ -983,7 +984,7 @@ static void digi_write_bulk_callback(struct urb *urb) dev_dbg(&port->dev, "digi_write_bulk_callback: oob callback\n"); spin_lock_irqsave(&priv->dp_port_lock, flags); priv->dp_write_urb_in_use = 0; - wake_up_interruptible(&port->write_wait); + wake_up_interruptible(&priv->write_wait); spin_unlock_irqrestore(&priv->dp_port_lock, flags); return; } @@ -1216,10 +1217,9 @@ static int digi_port_init(struct usb_serial_port *port, unsigned port_num) init_waitqueue_head(&priv->dp_transmit_idle_wait); init_waitqueue_head(&priv->dp_flush_wait); init_waitqueue_head(&priv->dp_close_wait); + init_waitqueue_head(&priv->write_wait); priv->dp_port = port; - init_waitqueue_head(&port->write_wait); - usb_set_serial_port_data(port, priv); return 0; diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 8e67eff9039f..1c09b922f8b0 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -62,7 +62,6 @@ * @bulk_out_endpointAddress: endpoint address for the bulk out pipe for this * port. * @flags: usb serial port flags - * @write_wait: a wait_queue_head_t used by the port. * @work: work queue entry for the line discipline waking up. * @dev: pointer to the serial device * @@ -108,7 +107,6 @@ struct usb_serial_port { int tx_bytes; unsigned long flags; - wait_queue_head_t write_wait; struct work_struct work; unsigned long sysrq; /* sysrq timeout */ struct device dev; -- cgit v1.2.3 From 4210d50f0b3e423e10a7a254b2a67f5c5318868e Mon Sep 17 00:00:00 2001 From: Isaac Hazan Date: Thu, 24 Sep 2020 11:43:58 +0300 Subject: thunderbolt: Add link_speed and link_width to XDomain Link speed and link width are needed for checking expected values in case of using a loopback service. Signed-off-by: Isaac Hazan Signed-off-by: Mika Westerberg Acked-by: Yehezkel Bernat Reviewed-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-bus-thunderbolt | 28 +++++++++++ drivers/thunderbolt/switch.c | 9 +++- drivers/thunderbolt/tb.h | 1 + drivers/thunderbolt/xdomain.c | 65 +++++++++++++++++++++++++ include/linux/thunderbolt.h | 4 ++ 5 files changed, 106 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-bus-thunderbolt b/Documentation/ABI/testing/sysfs-bus-thunderbolt index 0b4ab9e4b8f4..a91b4b24496e 100644 --- a/Documentation/ABI/testing/sysfs-bus-thunderbolt +++ b/Documentation/ABI/testing/sysfs-bus-thunderbolt @@ -1,3 +1,31 @@ +What: /sys/bus/thunderbolt/devices//rx_speed +Date: Feb 2021 +KernelVersion: 5.11 +Contact: Isaac Hazan +Description: This attribute reports the XDomain RX speed per lane. + All RX lanes run at the same speed. + +What: /sys/bus/thunderbolt/devices//rx_lanes +Date: Feb 2021 +KernelVersion: 5.11 +Contact: Isaac Hazan +Description: This attribute reports the number of RX lanes the XDomain + is using simultaneously through its upstream port. + +What: /sys/bus/thunderbolt/devices//tx_speed +Date: Feb 2021 +KernelVersion: 5.11 +Contact: Isaac Hazan +Description: This attribute reports the XDomain TX speed per lane. + All TX lanes run at the same speed. + +What: /sys/bus/thunderbolt/devices//tx_lanes +Date: Feb 2021 +KernelVersion: 5.11 +Contact: Isaac Hazan +Description: This attribute reports number of TX lanes the XDomain + is using simultaneously through its upstream port. + What: /sys/bus/thunderbolt/devices/.../domainX/boot_acl Date: Jun 2018 KernelVersion: 4.17 diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index c73bbfe69ba1..05a360901790 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -932,7 +932,14 @@ int tb_port_get_link_speed(struct tb_port *port) return speed == LANE_ADP_CS_1_CURRENT_SPEED_GEN3 ? 20 : 10; } -static int tb_port_get_link_width(struct tb_port *port) +/** + * tb_port_get_link_width() - Get current link width + * @port: Port to check (USB4 or CIO) + * + * Returns link width. Return values can be 1 (Single-Lane), 2 (Dual-Lane) + * or negative errno in case of failure. + */ +int tb_port_get_link_width(struct tb_port *port) { u32 val; int ret; diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index a9995e21b916..3a826315049e 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -862,6 +862,7 @@ struct tb_port *tb_next_port_on_path(struct tb_port *start, struct tb_port *end, (p) = tb_next_port_on_path((src), (dst), (p))) int tb_port_get_link_speed(struct tb_port *port); +int tb_port_get_link_width(struct tb_port *port); int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec); int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap); diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index da229ac4e471..83a315f96934 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -942,6 +942,43 @@ static void tb_xdomain_restore_paths(struct tb_xdomain *xd) } } +static inline struct tb_switch *tb_xdomain_parent(struct tb_xdomain *xd) +{ + return tb_to_switch(xd->dev.parent); +} + +static int tb_xdomain_update_link_attributes(struct tb_xdomain *xd) +{ + bool change = false; + struct tb_port *port; + int ret; + + port = tb_port_at(xd->route, tb_xdomain_parent(xd)); + + ret = tb_port_get_link_speed(port); + if (ret < 0) + return ret; + + if (xd->link_speed != ret) + change = true; + + xd->link_speed = ret; + + ret = tb_port_get_link_width(port); + if (ret < 0) + return ret; + + if (xd->link_width != ret) + change = true; + + xd->link_width = ret; + + if (change) + kobject_uevent(&xd->dev.kobj, KOBJ_CHANGE); + + return 0; +} + static void tb_xdomain_get_uuid(struct work_struct *work) { struct tb_xdomain *xd = container_of(work, typeof(*xd), @@ -1053,6 +1090,8 @@ static void tb_xdomain_get_properties(struct work_struct *work) xd->properties = dir; xd->property_block_gen = gen; + tb_xdomain_update_link_attributes(xd); + tb_xdomain_restore_paths(xd); mutex_unlock(&xd->lock); @@ -1159,9 +1198,35 @@ static ssize_t unique_id_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(unique_id); +static ssize_t speed_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev); + + return sprintf(buf, "%u.0 Gb/s\n", xd->link_speed); +} + +static DEVICE_ATTR(rx_speed, 0444, speed_show, NULL); +static DEVICE_ATTR(tx_speed, 0444, speed_show, NULL); + +static ssize_t lanes_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev); + + return sprintf(buf, "%u\n", xd->link_width); +} + +static DEVICE_ATTR(rx_lanes, 0444, lanes_show, NULL); +static DEVICE_ATTR(tx_lanes, 0444, lanes_show, NULL); + static struct attribute *xdomain_attrs[] = { &dev_attr_device.attr, &dev_attr_device_name.attr, + &dev_attr_rx_lanes.attr, + &dev_attr_rx_speed.attr, + &dev_attr_tx_lanes.attr, + &dev_attr_tx_speed.attr, &dev_attr_unique_id.attr, &dev_attr_vendor.attr, &dev_attr_vendor_name.attr, diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index 5db2b11ab085..e441af88ed77 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -179,6 +179,8 @@ void tb_unregister_property_dir(const char *key, struct tb_property_dir *dir); * @lock: Lock to serialize access to the following fields of this structure * @vendor_name: Name of the vendor (or %NULL if not known) * @device_name: Name of the device (or %NULL if not known) + * @link_speed: Speed of the link in Gb/s + * @link_width: Width of the link (1 or 2) * @is_unplugged: The XDomain is unplugged * @resume: The XDomain is being resumed * @needs_uuid: If the XDomain does not have @remote_uuid it will be @@ -223,6 +225,8 @@ struct tb_xdomain { struct mutex lock; const char *vendor_name; const char *device_name; + unsigned int link_speed; + unsigned int link_width; bool is_unplugged; bool resume; bool needs_uuid; -- cgit v1.2.3 From 5cc0df9ce10a860aaeac53f8df1cc8754c5c7b03 Mon Sep 17 00:00:00 2001 From: Isaac Hazan Date: Thu, 24 Sep 2020 11:44:01 +0300 Subject: thunderbolt: Add functions for enabling and disabling lane bonding on XDomain These can be used by service drivers to enable and disable lane bonding as needed. Signed-off-by: Isaac Hazan Signed-off-by: Mika Westerberg Acked-by: Yehezkel Bernat Reviewed-by: Greg Kroah-Hartman --- drivers/thunderbolt/switch.c | 24 ++++++++++++++-- drivers/thunderbolt/tb.h | 3 ++ drivers/thunderbolt/xdomain.c | 66 +++++++++++++++++++++++++++++++++++++++++++ include/linux/thunderbolt.h | 2 ++ 4 files changed, 92 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 05a360901790..cdfd8cccfe19 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -503,12 +503,13 @@ static void tb_dump_port(struct tb *tb, struct tb_regs_port_header *port) /** * tb_port_state() - get connectedness state of a port + * @port: the port to check * * The port must have a TB_CAP_PHY (i.e. it should be a real port). * * Return: Returns an enum tb_port_state on success or an error code on failure. */ -static int tb_port_state(struct tb_port *port) +int tb_port_state(struct tb_port *port) { struct tb_cap_phy phy; int res; @@ -1008,7 +1009,16 @@ static int tb_port_set_link_width(struct tb_port *port, unsigned int width) port->cap_phy + LANE_ADP_CS_1, 1); } -static int tb_port_lane_bonding_enable(struct tb_port *port) +/** + * tb_port_lane_bonding_enable() - Enable bonding on port + * @port: port to enable + * + * Enable bonding by setting the link width of the port and the + * other port in case of dual link port. + * + * Return: %0 in case of success and negative errno in case of error + */ +int tb_port_lane_bonding_enable(struct tb_port *port) { int ret; @@ -1038,7 +1048,15 @@ static int tb_port_lane_bonding_enable(struct tb_port *port) return 0; } -static void tb_port_lane_bonding_disable(struct tb_port *port) +/** + * tb_port_lane_bonding_disable() - Disable bonding on port + * @port: port to disable + * + * Disable bonding by setting the link width of the port and the + * other port in case of dual link port. + * + */ +void tb_port_lane_bonding_disable(struct tb_port *port) { port->dual_link_port->bonded = false; port->bonded = false; diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 3a826315049e..e98d3561648d 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -863,6 +863,9 @@ struct tb_port *tb_next_port_on_path(struct tb_port *start, struct tb_port *end, int tb_port_get_link_speed(struct tb_port *port); int tb_port_get_link_width(struct tb_port *port); +int tb_port_state(struct tb_port *port); +int tb_port_lane_bonding_enable(struct tb_port *port); +void tb_port_lane_bonding_disable(struct tb_port *port); int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec); int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap); diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index 83a315f96934..65108216bfe3 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -21,6 +22,7 @@ #define XDOMAIN_UUID_RETRIES 10 #define XDOMAIN_PROPERTIES_RETRIES 60 #define XDOMAIN_PROPERTIES_CHANGED_RETRIES 10 +#define XDOMAIN_BONDING_WAIT 100 /* ms */ struct xdomain_request_work { struct work_struct work; @@ -1443,6 +1445,70 @@ void tb_xdomain_remove(struct tb_xdomain *xd) device_unregister(&xd->dev); } +/** + * tb_xdomain_lane_bonding_enable() - Enable lane bonding on XDomain + * @xd: XDomain connection + * + * Lane bonding is disabled by default for XDomains. This function tries + * to enable bonding by first enabling the port and waiting for the CL0 + * state. + * + * Return: %0 in case of success and negative errno in case of error. + */ +int tb_xdomain_lane_bonding_enable(struct tb_xdomain *xd) +{ + struct tb_port *port; + int ret; + + port = tb_port_at(xd->route, tb_xdomain_parent(xd)); + if (!port->dual_link_port) + return -ENODEV; + + ret = tb_port_enable(port->dual_link_port); + if (ret) + return ret; + + ret = tb_wait_for_port(port->dual_link_port, true); + if (ret < 0) + return ret; + if (!ret) + return -ENOTCONN; + + ret = tb_port_lane_bonding_enable(port); + if (ret) { + tb_port_warn(port, "failed to enable lane bonding\n"); + return ret; + } + + tb_xdomain_update_link_attributes(xd); + + dev_dbg(&xd->dev, "lane bonding enabled\n"); + return 0; +} +EXPORT_SYMBOL_GPL(tb_xdomain_lane_bonding_enable); + +/** + * tb_xdomain_lane_bonding_disable() - Disable lane bonding + * @xd: XDomain connection + * + * Lane bonding is disabled by default for XDomains. If bonding has been + * enabled, this function can be used to disable it. + */ +void tb_xdomain_lane_bonding_disable(struct tb_xdomain *xd) +{ + struct tb_port *port; + + port = tb_port_at(xd->route, tb_xdomain_parent(xd)); + if (port->dual_link_port) { + tb_port_lane_bonding_disable(port); + tb_port_disable(port->dual_link_port); + tb_xdomain_update_link_attributes(xd); + + dev_dbg(&xd->dev, "lane bonding disabled\n"); + } +} +EXPORT_SYMBOL_GPL(tb_xdomain_lane_bonding_disable); + /** * tb_xdomain_enable_paths() - Enable DMA paths for XDomain connection * @xd: XDomain connection diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index e441af88ed77..0a747f92847e 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -247,6 +247,8 @@ struct tb_xdomain { u8 depth; }; +int tb_xdomain_lane_bonding_enable(struct tb_xdomain *xd); +void tb_xdomain_lane_bonding_disable(struct tb_xdomain *xd); int tb_xdomain_enable_paths(struct tb_xdomain *xd, u16 transmit_path, u16 transmit_ring, u16 receive_path, u16 receive_ring); -- cgit v1.2.3 From 407ac931aefda91ac90498c6b6e6893982173613 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 7 Oct 2020 17:53:44 +0300 Subject: thunderbolt: Create debugfs directory automatically for services This allows service drivers to use it as parent directory if they need to add their own debugfs entries. Signed-off-by: Mika Westerberg Acked-by: Yehezkel Bernat Reviewed-by: Greg Kroah-Hartman --- drivers/thunderbolt/debugfs.c | 24 ++++++++++++++++++++++++ drivers/thunderbolt/tb.h | 4 ++++ drivers/thunderbolt/xdomain.c | 3 +++ include/linux/thunderbolt.h | 4 ++++ 4 files changed, 35 insertions(+) (limited to 'include') diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c index 3680b2784ea1..e53ca8270acd 100644 --- a/drivers/thunderbolt/debugfs.c +++ b/drivers/thunderbolt/debugfs.c @@ -690,6 +690,30 @@ void tb_switch_debugfs_remove(struct tb_switch *sw) debugfs_remove_recursive(sw->debugfs_dir); } +/** + * tb_service_debugfs_init() - Add debugfs directory for service + * @svc: Thunderbolt service pointer + * + * Adds debugfs directory for service. + */ +void tb_service_debugfs_init(struct tb_service *svc) +{ + svc->debugfs_dir = debugfs_create_dir(dev_name(&svc->dev), + tb_debugfs_root); +} + +/** + * tb_service_debugfs_remove() - Remove service debugfs directory + * @svc: Thunderbolt service pointer + * + * Removes the previously created debugfs directory for @svc. + */ +void tb_service_debugfs_remove(struct tb_service *svc) +{ + debugfs_remove_recursive(svc->debugfs_dir); + svc->debugfs_dir = NULL; +} + void tb_debugfs_init(void) { tb_debugfs_root = debugfs_create_dir("thunderbolt", NULL); diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index e98d3561648d..a21000649009 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -1027,11 +1027,15 @@ void tb_debugfs_init(void); void tb_debugfs_exit(void); void tb_switch_debugfs_init(struct tb_switch *sw); void tb_switch_debugfs_remove(struct tb_switch *sw); +void tb_service_debugfs_init(struct tb_service *svc); +void tb_service_debugfs_remove(struct tb_service *svc); #else static inline void tb_debugfs_init(void) { } static inline void tb_debugfs_exit(void) { } static inline void tb_switch_debugfs_init(struct tb_switch *sw) { } static inline void tb_switch_debugfs_remove(struct tb_switch *sw) { } +static inline void tb_service_debugfs_init(struct tb_service *svc) { } +static inline void tb_service_debugfs_remove(struct tb_service *svc) { } #endif #ifdef CONFIG_USB4_KUNIT_TEST diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index 65108216bfe3..1a0491b461fd 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -779,6 +779,7 @@ static void tb_service_release(struct device *dev) struct tb_service *svc = container_of(dev, struct tb_service, dev); struct tb_xdomain *xd = tb_service_parent(svc); + tb_service_debugfs_remove(svc); ida_simple_remove(&xd->service_ids, svc->id); kfree(svc->key); kfree(svc); @@ -892,6 +893,8 @@ static void enumerate_services(struct tb_xdomain *xd) svc->dev.parent = &xd->dev; dev_set_name(&svc->dev, "%s.%d", dev_name(&xd->dev), svc->id); + tb_service_debugfs_init(svc); + if (device_register(&svc->dev)) { put_device(&svc->dev); break; diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index 0a747f92847e..a844fd5d96ab 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -350,6 +350,9 @@ void tb_unregister_protocol_handler(struct tb_protocol_handler *handler); * @prtcvers: Protocol version from the properties directory * @prtcrevs: Protocol software revision from the properties directory * @prtcstns: Protocol settings mask from the properties directory + * @debugfs_dir: Pointer to the service debugfs directory. Always created + * when debugfs is enabled. Can be used by service drivers to + * add their own entries under the service. * * Each domain exposes set of services it supports as collection of * properties. For each service there will be one corresponding @@ -363,6 +366,7 @@ struct tb_service { u32 prtcvers; u32 prtcrevs; u32 prtcstns; + struct dentry *debugfs_dir; }; static inline struct tb_service *tb_service_get(struct tb_service *svc) -- cgit v1.2.3 From afe704a2d0618ebdb559b5ddb059f6cdbfc78783 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 19 Oct 2020 19:15:20 +0300 Subject: thunderbolt: Add support for end-to-end flow control USB4 spec defines end-to-end (E2E) flow control that can be used between hosts to prevent overflow of a RX ring. We previously had this partially implemented but that code was removed with commit 53f13319d131 ("thunderbolt: Get rid of E2E workaround") with the idea that we add it back properly if there ever is need. Now that we are going to add DMA traffic test driver (in subsequent patches) this can be useful. For this reason we modify tb_ring_alloc_rx/tx() so that they accept RING_FLAG_E2E and configure the hardware ring accordingly. The RX side also requires passing TX HopID (e2e_tx_hop) used in the credit grant packets. Signed-off-by: Mika Westerberg Acked-by: Yehezkel Bernat Reviewed-by: Greg Kroah-Hartman --- drivers/net/thunderbolt.c | 2 +- drivers/thunderbolt/ctl.c | 4 ++-- drivers/thunderbolt/nhi.c | 36 ++++++++++++++++++++++++++++++++---- include/linux/thunderbolt.h | 8 +++++++- 4 files changed, 42 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/net/thunderbolt.c b/drivers/net/thunderbolt.c index 3160443ef3b9..d7b5f87eaa15 100644 --- a/drivers/net/thunderbolt.c +++ b/drivers/net/thunderbolt.c @@ -866,7 +866,7 @@ static int tbnet_open(struct net_device *dev) eof_mask = BIT(TBIP_PDF_FRAME_END); ring = tb_ring_alloc_rx(xd->tb->nhi, -1, TBNET_RING_SIZE, - RING_FLAG_FRAME, sof_mask, eof_mask, + RING_FLAG_FRAME, 0, sof_mask, eof_mask, tbnet_start_poll, net); if (!ring) { netdev_err(dev, "failed to allocate Rx ring\n"); diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index 9894b8f63064..1d86e27a0ef3 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -628,8 +628,8 @@ struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, event_cb cb, void *cb_data) if (!ctl->tx) goto err; - ctl->rx = tb_ring_alloc_rx(nhi, 0, 10, RING_FLAG_NO_SUSPEND, 0xffff, - 0xffff, NULL, NULL); + ctl->rx = tb_ring_alloc_rx(nhi, 0, 10, RING_FLAG_NO_SUSPEND, 0, 0xffff, + 0xffff, NULL, NULL); if (!ctl->rx) goto err; diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index 3f79baa54829..a69bc6b49405 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -483,7 +483,7 @@ err_unlock: static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, bool transmit, unsigned int flags, - u16 sof_mask, u16 eof_mask, + int e2e_tx_hop, u16 sof_mask, u16 eof_mask, void (*start_poll)(void *), void *poll_data) { @@ -506,6 +506,7 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, ring->is_tx = transmit; ring->size = size; ring->flags = flags; + ring->e2e_tx_hop = e2e_tx_hop; ring->sof_mask = sof_mask; ring->eof_mask = eof_mask; ring->head = 0; @@ -550,7 +551,7 @@ err_free_ring: struct tb_ring *tb_ring_alloc_tx(struct tb_nhi *nhi, int hop, int size, unsigned int flags) { - return tb_ring_alloc(nhi, hop, size, true, flags, 0, 0, NULL, NULL); + return tb_ring_alloc(nhi, hop, size, true, flags, 0, 0, 0, NULL, NULL); } EXPORT_SYMBOL_GPL(tb_ring_alloc_tx); @@ -560,6 +561,7 @@ EXPORT_SYMBOL_GPL(tb_ring_alloc_tx); * @hop: HopID (ring) to allocate. Pass %-1 for automatic allocation. * @size: Number of entries in the ring * @flags: Flags for the ring + * @e2e_tx_hop: Transmit HopID when E2E is enabled in @flags * @sof_mask: Mask of PDF values that start a frame * @eof_mask: Mask of PDF values that end a frame * @start_poll: If not %NULL the ring will call this function when an @@ -568,10 +570,11 @@ EXPORT_SYMBOL_GPL(tb_ring_alloc_tx); * @poll_data: Optional data passed to @start_poll */ struct tb_ring *tb_ring_alloc_rx(struct tb_nhi *nhi, int hop, int size, - unsigned int flags, u16 sof_mask, u16 eof_mask, + unsigned int flags, int e2e_tx_hop, + u16 sof_mask, u16 eof_mask, void (*start_poll)(void *), void *poll_data) { - return tb_ring_alloc(nhi, hop, size, false, flags, sof_mask, eof_mask, + return tb_ring_alloc(nhi, hop, size, false, flags, e2e_tx_hop, sof_mask, eof_mask, start_poll, poll_data); } EXPORT_SYMBOL_GPL(tb_ring_alloc_rx); @@ -618,6 +621,31 @@ void tb_ring_start(struct tb_ring *ring) ring_iowrite32options(ring, sof_eof_mask, 4); ring_iowrite32options(ring, flags, 0); } + + /* + * Now that the ring valid bit is set we can configure E2E if + * enabled for the ring. + */ + if (ring->flags & RING_FLAG_E2E) { + if (!ring->is_tx) { + u32 hop; + + hop = ring->e2e_tx_hop << REG_RX_OPTIONS_E2E_HOP_SHIFT; + hop &= REG_RX_OPTIONS_E2E_HOP_MASK; + flags |= hop; + + dev_dbg(&ring->nhi->pdev->dev, + "enabling E2E for %s %d with TX HopID %d\n", + RING_TYPE(ring), ring->hop, ring->e2e_tx_hop); + } else { + dev_dbg(&ring->nhi->pdev->dev, "enabling E2E for %s %d\n", + RING_TYPE(ring), ring->hop); + } + + flags |= RING_FLAG_E2E_FLOW_CONTROL; + ring_iowrite32options(ring, flags, 0); + } + ring_interrupt_active(ring, true); ring->running = true; err: diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index a844fd5d96ab..034dccf93955 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -481,6 +481,8 @@ struct tb_nhi { * @irq: MSI-X irq number if the ring uses MSI-X. %0 otherwise. * @vector: MSI-X vector number the ring uses (only set if @irq is > 0) * @flags: Ring specific flags + * @e2e_tx_hop: Transmit HopID when E2E is enabled. Only applicable to + * RX ring. For TX ring this should be set to %0. * @sof_mask: Bit mask used to detect start of frame PDF * @eof_mask: Bit mask used to detect end of frame PDF * @start_poll: Called when ring interrupt is triggered to start @@ -504,6 +506,7 @@ struct tb_ring { int irq; u8 vector; unsigned int flags; + int e2e_tx_hop; u16 sof_mask; u16 eof_mask; void (*start_poll)(void *data); @@ -514,6 +517,8 @@ struct tb_ring { #define RING_FLAG_NO_SUSPEND BIT(0) /* Configure the ring to be in frame mode */ #define RING_FLAG_FRAME BIT(1) +/* Enable end-to-end flow control */ +#define RING_FLAG_E2E BIT(2) struct ring_frame; typedef void (*ring_cb)(struct tb_ring *, struct ring_frame *, bool canceled); @@ -562,7 +567,8 @@ struct ring_frame { struct tb_ring *tb_ring_alloc_tx(struct tb_nhi *nhi, int hop, int size, unsigned int flags); struct tb_ring *tb_ring_alloc_rx(struct tb_nhi *nhi, int hop, int size, - unsigned int flags, u16 sof_mask, u16 eof_mask, + unsigned int flags, int e2e_tx_hop, + u16 sof_mask, u16 eof_mask, void (*start_poll)(void *), void *poll_data); void tb_ring_start(struct tb_ring *ring); void tb_ring_stop(struct tb_ring *ring); -- cgit v1.2.3 From e7018751d2e603381ae6028ba4dd21ec45ce38bb Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 13 Nov 2020 14:12:31 -0300 Subject: usb: host: ehci-mxc: Remove the driver The ehci-mxc driver was only used by i.MX non-DT platforms. Since 5.10-rc1, i.MX has been converted to a DT-only platform and all board files are gone. Remove the ehci-mxc driver as there are no more users at all. Acked-by: Alan Stern Signed-off-by: Fabio Estevam Link: https://lore.kernel.org/r/20201113171231.2205-1-festevam@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 7 - drivers/usb/host/Makefile | 1 - drivers/usb/host/ehci-mxc.c | 213 ----------------------------- include/linux/platform_data/usb-ehci-mxc.h | 14 -- 4 files changed, 235 deletions(-) delete mode 100644 drivers/usb/host/ehci-mxc.c delete mode 100644 include/linux/platform_data/usb-ehci-mxc.h (limited to 'include') diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index a633e9c15f7e..31e59309da1f 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -213,13 +213,6 @@ config USB_EHCI_FSL help Variation of ARC USB block used in some Freescale chips. -config USB_EHCI_MXC - tristate "Support for Freescale i.MX on-chip EHCI USB controller" - depends on ARCH_MXC || COMPILE_TEST - select USB_EHCI_ROOT_HUB_TT - help - Variation of ARC USB block used in some Freescale chips. - config USB_EHCI_HCD_NPCM7XX tristate "Support for Nuvoton NPCM7XX on-chip EHCI USB controller" depends on (USB_EHCI_HCD && ARCH_NPCM7XX) || COMPILE_TEST diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index d03f6f0764d2..c1b08703af10 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -40,7 +40,6 @@ obj-$(CONFIG_USB_PCI) += pci-quirks.o obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o obj-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o obj-$(CONFIG_USB_EHCI_HCD_PLATFORM) += ehci-platform.o -obj-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o obj-$(CONFIG_USB_EHCI_HCD_NPCM7XX) += ehci-npcm7xx.o obj-$(CONFIG_USB_EHCI_HCD_OMAP) += ehci-omap.o obj-$(CONFIG_USB_EHCI_HCD_ORION) += ehci-orion.o diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c deleted file mode 100644 index dc2676320527..000000000000 --- a/drivers/usb/host/ehci-mxc.c +++ /dev/null @@ -1,213 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 2008 Sascha Hauer , Pengutronix - * Copyright (c) 2009 Daniel Mack - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ehci.h" - -#define DRIVER_DESC "Freescale On-Chip EHCI Host driver" - -static const char hcd_name[] = "ehci-mxc"; - -#define ULPI_VIEWPORT_OFFSET 0x170 - -struct ehci_mxc_priv { - struct clk *usbclk, *ahbclk, *phyclk; -}; - -static struct hc_driver __read_mostly ehci_mxc_hc_driver; - -static const struct ehci_driver_overrides ehci_mxc_overrides __initconst = { - .extra_priv_size = sizeof(struct ehci_mxc_priv), -}; - -static int ehci_mxc_drv_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct mxc_usbh_platform_data *pdata = dev_get_platdata(dev); - struct usb_hcd *hcd; - struct resource *res; - int irq, ret; - struct ehci_mxc_priv *priv; - struct ehci_hcd *ehci; - - if (!pdata) { - dev_err(dev, "No platform data given, bailing out.\n"); - return -EINVAL; - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - hcd = usb_create_hcd(&ehci_mxc_hc_driver, dev, dev_name(dev)); - if (!hcd) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hcd->regs = devm_ioremap_resource(dev, res); - if (IS_ERR(hcd->regs)) { - ret = PTR_ERR(hcd->regs); - goto err_alloc; - } - hcd->rsrc_start = res->start; - hcd->rsrc_len = resource_size(res); - - hcd->has_tt = 1; - ehci = hcd_to_ehci(hcd); - priv = (struct ehci_mxc_priv *) ehci->priv; - - /* enable clocks */ - priv->usbclk = devm_clk_get(dev, "ipg"); - if (IS_ERR(priv->usbclk)) { - ret = PTR_ERR(priv->usbclk); - goto err_alloc; - } - clk_prepare_enable(priv->usbclk); - - priv->ahbclk = devm_clk_get(dev, "ahb"); - if (IS_ERR(priv->ahbclk)) { - ret = PTR_ERR(priv->ahbclk); - goto err_clk_ahb; - } - clk_prepare_enable(priv->ahbclk); - - /* "dr" device has its own clock on i.MX51 */ - priv->phyclk = devm_clk_get(dev, "phy"); - if (IS_ERR(priv->phyclk)) - priv->phyclk = NULL; - if (priv->phyclk) - clk_prepare_enable(priv->phyclk); - - /* call platform specific init function */ - if (pdata->init) { - ret = pdata->init(pdev); - if (ret) { - dev_err(dev, "platform init failed\n"); - goto err_init; - } - /* platforms need some time to settle changed IO settings */ - mdelay(10); - } - - /* EHCI registers start at offset 0x100 */ - ehci->caps = hcd->regs + 0x100; - ehci->regs = hcd->regs + 0x100 + - HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - - /* set up the PORTSCx register */ - ehci_writel(ehci, pdata->portsc, &ehci->regs->port_status[0]); - - /* is this really needed? */ - msleep(10); - - /* Initialize the transceiver */ - if (pdata->otg) { - pdata->otg->io_priv = hcd->regs + ULPI_VIEWPORT_OFFSET; - ret = usb_phy_init(pdata->otg); - if (ret) { - dev_err(dev, "unable to init transceiver, probably missing\n"); - ret = -ENODEV; - goto err_add; - } - ret = otg_set_vbus(pdata->otg->otg, 1); - if (ret) { - dev_err(dev, "unable to enable vbus on transceiver\n"); - goto err_add; - } - } - - platform_set_drvdata(pdev, hcd); - - ret = usb_add_hcd(hcd, irq, IRQF_SHARED); - if (ret) - goto err_add; - - device_wakeup_enable(hcd->self.controller); - return 0; - -err_add: - if (pdata && pdata->exit) - pdata->exit(pdev); -err_init: - if (priv->phyclk) - clk_disable_unprepare(priv->phyclk); - - clk_disable_unprepare(priv->ahbclk); -err_clk_ahb: - clk_disable_unprepare(priv->usbclk); -err_alloc: - usb_put_hcd(hcd); - return ret; -} - -static int ehci_mxc_drv_remove(struct platform_device *pdev) -{ - struct mxc_usbh_platform_data *pdata = dev_get_platdata(&pdev->dev); - struct usb_hcd *hcd = platform_get_drvdata(pdev); - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - struct ehci_mxc_priv *priv = (struct ehci_mxc_priv *) ehci->priv; - - usb_remove_hcd(hcd); - - if (pdata && pdata->exit) - pdata->exit(pdev); - - if (pdata && pdata->otg) - usb_phy_shutdown(pdata->otg); - - clk_disable_unprepare(priv->usbclk); - clk_disable_unprepare(priv->ahbclk); - - if (priv->phyclk) - clk_disable_unprepare(priv->phyclk); - - usb_put_hcd(hcd); - return 0; -} - -MODULE_ALIAS("platform:mxc-ehci"); - -static struct platform_driver ehci_mxc_driver = { - .probe = ehci_mxc_drv_probe, - .remove = ehci_mxc_drv_remove, - .shutdown = usb_hcd_platform_shutdown, - .driver = { - .name = "mxc-ehci", - }, -}; - -static int __init ehci_mxc_init(void) -{ - if (usb_disabled()) - return -ENODEV; - - pr_info("%s: " DRIVER_DESC "\n", hcd_name); - - ehci_init_driver(&ehci_mxc_hc_driver, &ehci_mxc_overrides); - return platform_driver_register(&ehci_mxc_driver); -} -module_init(ehci_mxc_init); - -static void __exit ehci_mxc_cleanup(void) -{ - platform_driver_unregister(&ehci_mxc_driver); -} -module_exit(ehci_mxc_cleanup); - -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_AUTHOR("Sascha Hauer"); -MODULE_LICENSE("GPL"); diff --git a/include/linux/platform_data/usb-ehci-mxc.h b/include/linux/platform_data/usb-ehci-mxc.h deleted file mode 100644 index ad9794d09bc8..000000000000 --- a/include/linux/platform_data/usb-ehci-mxc.h +++ /dev/null @@ -1,14 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __INCLUDE_ASM_ARCH_MXC_EHCI_H -#define __INCLUDE_ASM_ARCH_MXC_EHCI_H - -struct mxc_usbh_platform_data { - int (*init)(struct platform_device *pdev); - int (*exit)(struct platform_device *pdev); - - unsigned int portsc; - struct usb_phy *otg; -}; - -#endif /* __INCLUDE_ASM_ARCH_MXC_EHCI_H */ - -- cgit v1.2.3 From 56c62080d5b57dac2c2cdd4a83571450e38ca763 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 13 Nov 2020 22:27:04 +0100 Subject: usb: hcd.h: Remove RUN_CONTEXT The last user of RUN_CONTEXT was removed in commit 97c17beb3b668 ("[PATCH] ehci-hcd (1/2): portability (2.4), tasklet,") in the history.git repo. There are no users of RUN_CONTEXT, remove it. Signed-off-by: Sebastian Andrzej Siewior Link: https://lore.kernel.org/r/20201113212704.2243807-1-bigeasy@linutronix.de Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/hcd.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'include') diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 3dbb42c637c1..96281cd50ff6 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -734,10 +734,6 @@ static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb, /* random stuff */ -#define RUN_CONTEXT (in_irq() ? "in_irq" \ - : (in_interrupt() ? "in_interrupt" : "can sleep")) - - /* This rwsem is for use only by the hub driver and ehci-hcd. * Nobody else should touch it. */ -- cgit v1.2.3 From 13d40ff85da8a85935e3fd47061dee71a02af0d4 Mon Sep 17 00:00:00 2001 From: Utkarsh Patel Date: Fri, 13 Nov 2020 12:24:56 -0800 Subject: usb: typec: Correct the bit values for the Thunderbolt rounded/non-rounded cable support Rounded and non-rounded Thunderbolt cables are represented by two bits as per USB Type-C Connector specification v2.0 section F.2.6. Corrected that in the Thunderbolt 3 cable discover mode VDO. Signed-off-by: Utkarsh Patel Reviewed-by: Heikki Krogerus -- Changes in v2: - Removed the fixes tag as there is no functional implication. -- Link: https://lore.kernel.org/r/20201113202503.6559-2-utkarsh.h.patel@intel.com Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/typec_tbt.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/linux/usb/typec_tbt.h b/include/linux/usb/typec_tbt.h index 47c2d501ddce..aad648d14bb3 100644 --- a/include/linux/usb/typec_tbt.h +++ b/include/linux/usb/typec_tbt.h @@ -40,11 +40,16 @@ struct typec_thunderbolt_data { #define TBT_CABLE_USB3_PASSIVE 2 #define TBT_CABLE_10_AND_20GBPS 3 #define TBT_CABLE_ROUNDED BIT(19) +#define TBT_CABLE_ROUNDED_SUPPORT(_vdo_) \ + (((_vdo_) & GENMASK(20, 19)) >> 19) +#define TBT_GEN3_NON_ROUNDED 0 +#define TBT_GEN3_GEN4_ROUNDED_NON_ROUNDED 1 #define TBT_CABLE_OPTICAL BIT(21) #define TBT_CABLE_RETIMER BIT(22) #define TBT_CABLE_LINK_TRAINING BIT(23) #define TBT_SET_CABLE_SPEED(_s_) (((_s_) & GENMASK(2, 0)) << 16) +#define TBT_SET_CABLE_ROUNDED(_g_) (((_g_) & GENMASK(1, 0)) << 19) /* TBT3 Device Enter Mode VDO bits */ #define TBT_ENTER_MODE_CABLE_SPEED(s) TBT_SET_CABLE_SPEED(s) -- cgit v1.2.3 From 523a97aa3b756c1e0efe33ae48d450f8b0052073 Mon Sep 17 00:00:00 2001 From: Utkarsh Patel Date: Fri, 13 Nov 2020 12:24:59 -0800 Subject: usb: typec: Remove one bit support for the Thunderbolt rounded/non-rounded cable Two bits support for the Thunderbolt rounded/non-rounded cable has been added to the header file. Hence, removing unused TBT_CABLE_ROUNDED definition from the header file. Signed-off-by: Utkarsh Patel Reviewed-by: Heikki Krogerus -- changes in v2: - Removed the fixes tag as there is no functional implication. -- Link: https://lore.kernel.org/r/20201113202503.6559-5-utkarsh.h.patel@intel.com Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/typec_tbt.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/usb/typec_tbt.h b/include/linux/usb/typec_tbt.h index aad648d14bb3..63dd44b72e0c 100644 --- a/include/linux/usb/typec_tbt.h +++ b/include/linux/usb/typec_tbt.h @@ -39,7 +39,6 @@ struct typec_thunderbolt_data { #define TBT_CABLE_USB3_GEN1 1 #define TBT_CABLE_USB3_PASSIVE 2 #define TBT_CABLE_10_AND_20GBPS 3 -#define TBT_CABLE_ROUNDED BIT(19) #define TBT_CABLE_ROUNDED_SUPPORT(_vdo_) \ (((_vdo_) & GENMASK(20, 19)) >> 19) #define TBT_GEN3_NON_ROUNDED 0 -- cgit v1.2.3 From 8a5ca78f603977d6b9b2d7dcb3b1b6ef601d3cc7 Mon Sep 17 00:00:00 2001 From: Prashant Malani Date: Mon, 16 Nov 2020 12:11:38 -0800 Subject: usb: pd: Add captive Type C cable type The USB Power Delivery Specification R3.0 adds a captive cable type to the "USB Type-C plug to USB Type-C/Captive" field (Bits 19-18, Passive/Active Cable VDO, Table 6-38 & 6-39). Add the corresponding definition to the Cable VDO header. Also add a helper macro to get the Type C cable connector type, when provided the cable VDO. Cc: Heikki Krogerus Signed-off-by: Prashant Malani Reviewed-by: Benson Leung Reviewed-by: Greg Kroah-Hartman Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20201116201150.2919178-2-pmalani@chromium.org Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/pd_vdo.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/usb/pd_vdo.h b/include/linux/usb/pd_vdo.h index 68bdc4e2f5a9..8c5cb5830754 100644 --- a/include/linux/usb/pd_vdo.h +++ b/include/linux/usb/pd_vdo.h @@ -177,7 +177,7 @@ * <31:28> :: Cable HW version * <27:24> :: Cable FW version * <23:20> :: Reserved, Shall be set to zero - * <19:18> :: type-C to Type-A/B/C (00b == A, 01 == B, 10 == C) + * <19:18> :: type-C to Type-A/B/C/Captive (00b == A, 01 == B, 10 == C, 11 == Captive) * <17> :: Type-C to Plug/Receptacle (0b == plug, 1b == receptacle) * <16:13> :: cable latency (0001 == <10ns(~1m length)) * <12:11> :: cable termination type (11b == both ends active VCONN req) @@ -193,6 +193,7 @@ #define CABLE_ATYPE 0 #define CABLE_BTYPE 1 #define CABLE_CTYPE 2 +#define CABLE_CAPTIVE 3 #define CABLE_PLUG 0 #define CABLE_RECEPTACLE 1 #define CABLE_CURR_1A5 0 @@ -208,6 +209,7 @@ | (tx1d) << 10 | (tx2d) << 9 | (rx1d) << 8 | (rx2d) << 7 \ | ((cur) & 0x3) << 5 | (vps) << 4 | (sopp) << 3 \ | ((usbss) & 0x7)) +#define VDO_TYPEC_CABLE_TYPE(vdo) (((vdo) >> 18) & 0x3) /* * AMA VDO -- cgit v1.2.3 From a0ccdc4a77a1b36b682ae60361879eca0a0f88d6 Mon Sep 17 00:00:00 2001 From: Prashant Malani Date: Mon, 16 Nov 2020 12:11:40 -0800 Subject: usb: typec: Add number of altmodes partner attr Add a user-visible attribute for the number of alternate modes available in a partner. This allows userspace to determine whether there are any remaining alternate modes left to be registered by the kernel driver. It can begin executing any policy state machine after all available alternate modes have been registered with the connector class framework. This value is set to "-1" initially, signifying that a valid number of alternate modes haven't been set for the partner. Also add a sysfs file which exposes this attribute. The file remains hidden as long as the attribute value is -1. Cc: Benson Leung Cc: Heikki Krogerus Signed-off-by: Prashant Malani Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20201116201150.2919178-3-pmalani@chromium.org Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-class-typec | 8 ++++ drivers/usb/typec/class.c | 66 ++++++++++++++++++++++++++++- include/linux/usb/typec.h | 1 + 3 files changed, 74 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-class-typec b/Documentation/ABI/testing/sysfs-class-typec index b7794e02ad20..a195247d83ee 100644 --- a/Documentation/ABI/testing/sysfs-class-typec +++ b/Documentation/ABI/testing/sysfs-class-typec @@ -139,6 +139,14 @@ Description: Shows if the partner supports USB Power Delivery communication: Valid values: yes, no +What: /sys/class/typec/-partner/number_of_alternate_modes +Date: November 2020 +Contact: Prashant Malani +Description: + Shows the number of alternate modes which are advertised by the partner + during Power Delivery discovery. This file remains hidden until a value + greater than or equal to 0 is set by Type C port driver. + What: /sys/class/typec/-partner>/identity/ Date: April 2017 Contact: Heikki Krogerus diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 35eec707cb51..c7412ddbd311 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -33,6 +33,7 @@ struct typec_partner { struct usb_pd_identity *identity; enum typec_accessory accessory; struct ida mode_ids; + int num_altmodes; }; struct typec_port { @@ -532,12 +533,43 @@ static ssize_t supports_usb_power_delivery_show(struct device *dev, } static DEVICE_ATTR_RO(supports_usb_power_delivery); +static ssize_t number_of_alternate_modes_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct typec_partner *p = to_typec_partner(dev); + + return sysfs_emit(buf, "%d\n", p->num_altmodes); +} +static DEVICE_ATTR_RO(number_of_alternate_modes); + static struct attribute *typec_partner_attrs[] = { &dev_attr_accessory_mode.attr, &dev_attr_supports_usb_power_delivery.attr, + &dev_attr_number_of_alternate_modes.attr, + NULL +}; + +static umode_t typec_partner_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) +{ + struct typec_partner *partner = to_typec_partner(kobj_to_dev(kobj)); + + if (attr == &dev_attr_number_of_alternate_modes.attr) { + if (partner->num_altmodes < 0) + return 0; + } + + return attr->mode; +} + +static struct attribute_group typec_partner_group = { + .is_visible = typec_partner_attr_is_visible, + .attrs = typec_partner_attrs +}; + +static const struct attribute_group *typec_partner_groups[] = { + &typec_partner_group, NULL }; -ATTRIBUTE_GROUPS(typec_partner); static void typec_partner_release(struct device *dev) { @@ -570,6 +602,37 @@ int typec_partner_set_identity(struct typec_partner *partner) } EXPORT_SYMBOL_GPL(typec_partner_set_identity); +/** + * typec_partner_set_num_altmodes - Set the number of available partner altmodes + * @partner: The partner to be updated. + * @num_alt_modes: The number of altmodes we want to specify as available. + * + * This routine is used to report the number of alternate modes supported by the + * partner. This value is *not* enforced in alternate mode registration routines. + * + * @partner.num_altmodes is set to -1 on partner registration, denoting that + * a valid value has not been set for it yet. + * + * Returns 0 on success or negative error number on failure. + */ +int typec_partner_set_num_altmodes(struct typec_partner *partner, int num_altmodes) +{ + int ret; + + if (num_altmodes < 0) + return -EINVAL; + + partner->num_altmodes = num_altmodes; + ret = sysfs_update_group(&partner->dev.kobj, &typec_partner_group); + if (ret < 0) + return ret; + + sysfs_notify(&partner->dev.kobj, NULL, "number_of_alternate_modes"); + + return 0; +} +EXPORT_SYMBOL_GPL(typec_partner_set_num_altmodes); + /** * typec_partner_register_altmode - Register USB Type-C Partner Alternate Mode * @partner: USB Type-C Partner that supports the alternate mode @@ -612,6 +675,7 @@ struct typec_partner *typec_register_partner(struct typec_port *port, ida_init(&partner->mode_ids); partner->usb_pd = desc->usb_pd; partner->accessory = desc->accessory; + partner->num_altmodes = -1; if (desc->identity) { /* diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index 6be558045942..bc6b1a71cb8a 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -126,6 +126,7 @@ struct typec_altmode_desc { enum typec_port_data roles; }; +int typec_partner_set_num_altmodes(struct typec_partner *partner, int num_altmodes); struct typec_altmode *typec_partner_register_altmode(struct typec_partner *partner, const struct typec_altmode_desc *desc); -- cgit v1.2.3 From a30a00e37ceb094f949e4d96c2c586e6503b5d1d Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Wed, 28 Oct 2020 23:31:32 -0700 Subject: usb: typec: tcpm: frs sourcing vbus callback During FRS hardware autonomously starts to source vbus. Provide callback to perform chip specific operations. Signed-off-by: Badhri Jagan Sridharan Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20201029063138.1429760-5-badhri@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 9 +++++++++ include/linux/usb/tcpm.h | 4 ++++ 2 files changed, 13 insertions(+) (limited to 'include') diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index a091a63758f4..c196bd4b8a37 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -4091,7 +4091,16 @@ static void _tcpm_pd_vbus_on(struct tcpm_port *port) case SRC_TRY_DEBOUNCE: /* Do nothing, waiting for sink detection */ break; + case FR_SWAP_SEND: + case FR_SWAP_SEND_TIMEOUT: + case FR_SWAP_SNK_SRC_TRANSITION_TO_OFF: + case FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED: + if (port->tcpc->frs_sourcing_vbus) + port->tcpc->frs_sourcing_vbus(port->tcpc); + break; case FR_SWAP_SNK_SRC_NEW_SINK_READY: + if (port->tcpc->frs_sourcing_vbus) + port->tcpc->frs_sourcing_vbus(port->tcpc); tcpm_set_state(port, FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED, 0); break; diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h index 09762d26fa0c..7303f518ba49 100644 --- a/include/linux/usb/tcpm.h +++ b/include/linux/usb/tcpm.h @@ -83,6 +83,9 @@ enum tcpm_transmit_type { * Optional; Called to enable/disable PD 3.0 fast role swap. * Enabling frs is accessory dependent as not all PD3.0 * accessories support fast role swap. + * @frs_sourcing_vbus: + * Optional; Called to notify that vbus is now being sourced. + * Low level drivers can perform chip specific operations, if any. */ struct tcpc_dev { struct fwnode_handle *fwnode; @@ -109,6 +112,7 @@ struct tcpc_dev { const struct pd_message *msg); int (*set_bist_data)(struct tcpc_dev *dev, bool on); int (*enable_frs)(struct tcpc_dev *dev, bool enable); + void (*frs_sourcing_vbus)(struct tcpc_dev *dev); }; struct tcpm_port; -- cgit v1.2.3 From f321a02caebdd0c56e167610cda2fa148cd96e8b Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Wed, 28 Oct 2020 23:31:35 -0700 Subject: usb: typec: tcpm: Implement enabling Auto Discharge disconnect support TCPCI spec allows TCPC hardware to autonomously discharge the vbus capacitance upon disconnect. The expectation is that the TCPM enables AutoDischargeDisconnect while entering SNK/SRC_ATTACHED states. Hardware then automously discharges vbus when the vbus falls below a certain threshold i.e. VBUS_SINK_DISCONNECT_THRESHOLD. Apart from enabling the vbus discharge circuit, AutoDischargeDisconnect is also used a flag to move TCPCI based TCPC implementations into Attached.Snk/Attached.Src state as mentioned in Figure 4-15. TCPC State Diagram before a Connection of the USB Type-C Port Controller Interface Specification. In such TCPC implementations, setting AutoDischargeDisconnect would prevent TCPC into entering "Connection_Invalid" state as well. Signed-off-by: Badhri Jagan Sridharan Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20201029063138.1429760-8-badhri@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 60 ++++++++++++++++++++++++++++++++++++++++--- include/linux/usb/tcpm.h | 15 +++++++++++ 2 files changed, 71 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index c196bd4b8a37..4aac0efdb720 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -1706,6 +1706,24 @@ static void tcpm_handle_alert(struct tcpm_port *port, const __le32 *payload, } } +static int tcpm_set_auto_vbus_discharge_threshold(struct tcpm_port *port, + enum typec_pwr_opmode mode, bool pps_active, + u32 requested_vbus_voltage) +{ + int ret; + + if (!port->tcpc->set_auto_vbus_discharge_threshold) + return 0; + + ret = port->tcpc->set_auto_vbus_discharge_threshold(port->tcpc, mode, pps_active, + requested_vbus_voltage); + tcpm_log_force(port, + "set_auto_vbus_discharge_threshold mode:%d pps_active:%c vbus:%u ret:%d", + mode, pps_active ? 'y' : 'n', requested_vbus_voltage, ret); + + return ret; +} + static void tcpm_pd_data_request(struct tcpm_port *port, const struct pd_message *msg) { @@ -1876,6 +1894,10 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, port->current_limit, port->supply_voltage); port->explicit_contract = true; + tcpm_set_auto_vbus_discharge_threshold(port, + TYPEC_PWR_MODE_PD, + port->pps_data.active, + port->supply_voltage); tcpm_set_state(port, SNK_READY, 0); } else { /* @@ -2790,8 +2812,12 @@ static int tcpm_src_attach(struct tcpm_port *port) if (ret < 0) return ret; - ret = tcpm_set_roles(port, true, TYPEC_SOURCE, - tcpm_data_role_for_source(port)); + if (port->tcpc->enable_auto_vbus_discharge) { + ret = port->tcpc->enable_auto_vbus_discharge(port->tcpc, true); + tcpm_log_force(port, "enable vbus discharge ret:%d", ret); + } + + ret = tcpm_set_roles(port, true, TYPEC_SOURCE, tcpm_data_role_for_source(port)); if (ret < 0) return ret; @@ -2858,6 +2884,12 @@ static void tcpm_unregister_altmodes(struct tcpm_port *port) static void tcpm_reset_port(struct tcpm_port *port) { + int ret; + + if (port->tcpc->enable_auto_vbus_discharge) { + ret = port->tcpc->enable_auto_vbus_discharge(port->tcpc, false); + tcpm_log_force(port, "Disable vbus discharge ret:%d", ret); + } tcpm_unregister_altmodes(port); tcpm_typec_disconnect(port); port->attached = false; @@ -2922,8 +2954,13 @@ static int tcpm_snk_attach(struct tcpm_port *port) if (ret < 0) return ret; - ret = tcpm_set_roles(port, true, TYPEC_SINK, - tcpm_data_role_for_sink(port)); + if (port->tcpc->enable_auto_vbus_discharge) { + tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, false, VSAFE5V); + ret = port->tcpc->enable_auto_vbus_discharge(port->tcpc, true); + tcpm_log_force(port, "enable vbus discharge ret:%d", ret); + } + + ret = tcpm_set_roles(port, true, TYPEC_SINK, tcpm_data_role_for_sink(port)); if (ret < 0) return ret; @@ -3507,6 +3544,8 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, SRC_UNATTACHED, PD_T_PS_SOURCE_ON); break; case SNK_HARD_RESET_SINK_OFF: + /* Do not discharge/disconnect during hard reseet */ + tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, false, 0); memset(&port->pps_data, 0, sizeof(port->pps_data)); tcpm_set_vconn(port, false); if (port->pd_capable) @@ -3549,6 +3588,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_charge(port, true); } tcpm_set_attached_state(port, true); + tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, false, VSAFE5V); tcpm_set_state(port, SNK_STARTUP, 0); break; @@ -3650,6 +3690,10 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, PR_SWAP_SNK_SRC_SINK_OFF, 0); break; case PR_SWAP_SRC_SNK_TRANSITION_OFF: + /* + * Prevent vbus discharge circuit from turning on during PR_SWAP + * as this is not a disconnect. + */ tcpm_set_vbus(port, false); port->explicit_contract = false; /* allow time for Vbus discharge, must be < tSrcSwapStdby */ @@ -3678,9 +3722,17 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state_cond(port, SNK_UNATTACHED, PD_T_PS_SOURCE_ON); break; case PR_SWAP_SRC_SNK_SINK_ON: + /* Set the vbus disconnect threshold for implicit contract */ + tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, false, VSAFE5V); tcpm_set_state(port, SNK_STARTUP, 0); break; case PR_SWAP_SNK_SRC_SINK_OFF: + /* + * Prevent vbus discharge circuit from turning on during PR_SWAP + * as this is not a disconnect. + */ + tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, + port->pps_data.active, 0); tcpm_set_charge(port, false); tcpm_set_state(port, hard_reset_state(port), PD_T_PS_SOURCE_OFF); diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h index 7303f518ba49..e68aaa12886f 100644 --- a/include/linux/usb/tcpm.h +++ b/include/linux/usb/tcpm.h @@ -86,6 +86,18 @@ enum tcpm_transmit_type { * @frs_sourcing_vbus: * Optional; Called to notify that vbus is now being sourced. * Low level drivers can perform chip specific operations, if any. + * @enable_auto_vbus_discharge: + * Optional; TCPCI spec based TCPC implementations can optionally + * support hardware to autonomously dischrge vbus upon disconnecting + * as sink or source. TCPM signals TCPC to enable the mechanism upon + * entering connected state and signals disabling upon disconnect. + * @set_auto_vbus_discharge_threshold: + * Mandatory when enable_auto_vbus_discharge is implemented. TCPM + * calls this function to allow lower levels drivers to program the + * vbus threshold voltage below which the vbus discharge circuit + * will be turned on. requested_vbus_voltage is set to 0 when vbus + * is going to disappear knowingly i.e. during PR_SWAP and + * HARD_RESET etc. */ struct tcpc_dev { struct fwnode_handle *fwnode; @@ -113,6 +125,9 @@ struct tcpc_dev { int (*set_bist_data)(struct tcpc_dev *dev, bool on); int (*enable_frs)(struct tcpc_dev *dev, bool enable); void (*frs_sourcing_vbus)(struct tcpc_dev *dev); + int (*enable_auto_vbus_discharge)(struct tcpc_dev *dev, bool enable); + int (*set_auto_vbus_discharge_threshold)(struct tcpc_dev *dev, enum typec_pwr_opmode mode, + bool pps_active, u32 requested_vbus_voltage); }; struct tcpm_port; -- cgit v1.2.3 From e1e52361c61afdf81d81cfbbfa3ce08971e60f50 Mon Sep 17 00:00:00 2001 From: Prashant Malani Date: Mon, 16 Nov 2020 12:11:42 -0800 Subject: usb: typec: Add plug num_altmodes sysfs attr Add a field to the typec_plug struct to record the number of available altmodes as well as the corresponding sysfs attribute to expose this to userspace. This allows userspace to determine whether there are any remaining alternate modes left to be registered by the kernel driver. It can begin executing any policy state machine after all available alternate modes have been registered with the connector class framework. This value is set to "-1" initially, signifying that a valid number of alternate modes haven't been set for the plug. The sysfs file remains hidden as long as the attribute value is -1. We re-use the partner attribute for number_of_alternate_modes since the usage and name is similar, and update the corresponding *_show() command to support both partner and plugs. Signed-off-by: Prashant Malani Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20201116201150.2919178-4-pmalani@chromium.org Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-class-typec | 9 ++++ drivers/usb/typec/class.c | 77 ++++++++++++++++++++++++++++- include/linux/usb/typec.h | 1 + 3 files changed, 85 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-class-typec b/Documentation/ABI/testing/sysfs-class-typec index a195247d83ee..4eccb343fc7b 100644 --- a/Documentation/ABI/testing/sysfs-class-typec +++ b/Documentation/ABI/testing/sysfs-class-typec @@ -210,6 +210,15 @@ Description: - type-c - captive +What: /sys/class/typec/-/number_of_alternate_modes +Date: November 2020 +Contact: Prashant Malani +Description: + Shows the number of alternate modes which are advertised by the plug + associated with a particular cable during Power Delivery discovery. + This file remains hidden until a value greater than or equal to 0 + is set by Type C port driver. + What: /sys/class/typec/-cable/identity/ Date: April 2017 Contact: Heikki Krogerus diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index c7412ddbd311..e68798599ca8 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -18,6 +18,7 @@ struct typec_plug { struct device dev; enum typec_plug_index index; struct ida mode_ids; + int num_altmodes; }; struct typec_cable { @@ -536,9 +537,21 @@ static DEVICE_ATTR_RO(supports_usb_power_delivery); static ssize_t number_of_alternate_modes_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct typec_partner *p = to_typec_partner(dev); + struct typec_partner *partner; + struct typec_plug *plug; + int num_altmodes; + + if (is_typec_partner(dev)) { + partner = to_typec_partner(dev); + num_altmodes = partner->num_altmodes; + } else if (is_typec_plug(dev)) { + plug = to_typec_plug(dev); + num_altmodes = plug->num_altmodes; + } else { + return 0; + } - return sysfs_emit(buf, "%d\n", p->num_altmodes); + return sysfs_emit(buf, "%d\n", num_altmodes); } static DEVICE_ATTR_RO(number_of_alternate_modes); @@ -726,11 +739,70 @@ static void typec_plug_release(struct device *dev) kfree(plug); } +static struct attribute *typec_plug_attrs[] = { + &dev_attr_number_of_alternate_modes.attr, + NULL +}; + +static umode_t typec_plug_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) +{ + struct typec_plug *plug = to_typec_plug(kobj_to_dev(kobj)); + + if (attr == &dev_attr_number_of_alternate_modes.attr) { + if (plug->num_altmodes < 0) + return 0; + } + + return attr->mode; +} + +static struct attribute_group typec_plug_group = { + .is_visible = typec_plug_attr_is_visible, + .attrs = typec_plug_attrs +}; + +static const struct attribute_group *typec_plug_groups[] = { + &typec_plug_group, + NULL +}; + static const struct device_type typec_plug_dev_type = { .name = "typec_plug", + .groups = typec_plug_groups, .release = typec_plug_release, }; +/** + * typec_plug_set_num_altmodes - Set the number of available plug altmodes + * @plug: The plug to be updated. + * @num_altmodes: The number of altmodes we want to specify as available. + * + * This routine is used to report the number of alternate modes supported by the + * plug. This value is *not* enforced in alternate mode registration routines. + * + * @plug.num_altmodes is set to -1 on plug registration, denoting that + * a valid value has not been set for it yet. + * + * Returns 0 on success or negative error number on failure. + */ +int typec_plug_set_num_altmodes(struct typec_plug *plug, int num_altmodes) +{ + int ret; + + if (num_altmodes < 0) + return -EINVAL; + + plug->num_altmodes = num_altmodes; + ret = sysfs_update_group(&plug->dev.kobj, &typec_plug_group); + if (ret < 0) + return ret; + + sysfs_notify(&plug->dev.kobj, NULL, "number_of_alternate_modes"); + + return 0; +} +EXPORT_SYMBOL_GPL(typec_plug_set_num_altmodes); + /** * typec_plug_register_altmode - Register USB Type-C Cable Plug Alternate Mode * @plug: USB Type-C Cable Plug that supports the alternate mode @@ -776,6 +848,7 @@ struct typec_plug *typec_register_plug(struct typec_cable *cable, sprintf(name, "plug%d", desc->index); ida_init(&plug->mode_ids); + plug->num_altmodes = -1; plug->index = desc->index; plug->dev.class = typec_class; plug->dev.parent = &cable->dev; diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index bc6b1a71cb8a..54475323f83b 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -130,6 +130,7 @@ int typec_partner_set_num_altmodes(struct typec_partner *partner, int num_altmod struct typec_altmode *typec_partner_register_altmode(struct typec_partner *partner, const struct typec_altmode_desc *desc); +int typec_plug_set_num_altmodes(struct typec_plug *plug, int num_altmodes); struct typec_altmode *typec_plug_register_altmode(struct typec_plug *plug, const struct typec_altmode_desc *desc); -- cgit v1.2.3 From 7656ca71b0ba04ae7437afe3d046e9134442678e Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 25 Nov 2020 15:06:41 +0300 Subject: usb: pd: DFP product types USB Power Delivery Specification R3.0 introduced separate field for the DFP product type to the ID Header VDO. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20201125120642.37156-2-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/pd_vdo.h | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/usb/pd_vdo.h b/include/linux/usb/pd_vdo.h index 8c5cb5830754..8c08eeb9a74b 100644 --- a/include/linux/usb/pd_vdo.h +++ b/include/linux/usb/pd_vdo.h @@ -103,17 +103,25 @@ * -------------------- * <31> :: data capable as a USB host * <30> :: data capable as a USB device - * <29:27> :: product type + * <29:27> :: product type (UFP / Cable) * <26> :: modal operation supported (1b == yes) - * <25:16> :: Reserved, Shall be set to zero + * <25:16> :: product type (DFP) * <15:0> :: USB-IF assigned VID for this cable vendor */ #define IDH_PTYPE_UNDEF 0 #define IDH_PTYPE_HUB 1 #define IDH_PTYPE_PERIPH 2 +#define IDH_PTYPE_PSD 3 +#define IDH_PTYPE_AMA 5 + #define IDH_PTYPE_PCABLE 3 #define IDH_PTYPE_ACABLE 4 -#define IDH_PTYPE_AMA 5 + +#define IDH_PTYPE_DFP_UNDEF 0 +#define IDH_PTYPE_DFP_HUB 1 +#define IDH_PTYPE_DFP_HOST 2 +#define IDH_PTYPE_DFP_PB 3 +#define IDH_PTYPE_DFP_AMC 4 #define VDO_IDH(usbh, usbd, ptype, is_modal, vid) \ ((usbh) << 31 | (usbd) << 30 | ((ptype) & 0x7) << 27 \ @@ -122,6 +130,7 @@ #define PD_IDH_PTYPE(vdo) (((vdo) >> 27) & 0x7) #define PD_IDH_VID(vdo) ((vdo) & 0xffff) #define PD_IDH_MODAL_SUPP(vdo) ((vdo) & (1 << 26)) +#define PD_IDH_DFP_PTYPE(vdo) (((vdo) >> 23) & 0x7) /* * Cert Stat VDO -- cgit v1.2.3 From 11e5e568ceed7c8c570313a14fa96c72f21dad31 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Tue, 24 Nov 2020 17:48:04 -0800 Subject: usb: typec: tcpm: Stay in SNK_TRY_WAIT_DEBOUNCE_CHECK_VBUS till Rp is seen TD.4.7.3. Try SNK DRP Connect Try.SRC DRP fails. The compliance tester mimics being a Try.SRC USB-C port. The failure is due to TCPM exiting SNK_TRY_WAIT_DEBOUNCE_CHECK_VBUS when VBUS is not present eventhough when SNK.Rp is seen. Exit to SRC_TRYWAIT from SNK_TRY_WAIT_DEBOUNCE_CHECK_VBUS only when SNK.Rp is not seen for PD_T_TRY_CC_DEBOUNCE. >From the spec: The port shall then transition to Attached.SNK when the SNK.Rp state is detected on exactly one of the CC1 or CC2 pins for at least tTryCCDebounce and VBUS is detected. Alternatively, the port shall transition to TryWait.SRC if SNK.Rp state is not detected for tTryCCDebounce. Reviewed-by: Guenter Roeck Signed-off-by: Badhri Jagan Sridharan Link: https://lore.kernel.org/r/20201125014804.1596719-1-badhri@google.com Acked-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 18 +++++++++++++----- include/linux/usb/pd.h | 1 + 2 files changed, 14 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 277b9d4d9c84..3bbc1f10af49 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -3124,15 +3124,13 @@ static void run_state_machine(struct tcpm_port *port) break; case SNK_TRY_WAIT_DEBOUNCE: tcpm_set_state(port, SNK_TRY_WAIT_DEBOUNCE_CHECK_VBUS, - PD_T_PD_DEBOUNCE); + PD_T_TRY_CC_DEBOUNCE); break; case SNK_TRY_WAIT_DEBOUNCE_CHECK_VBUS: - if (port->vbus_present && tcpm_port_is_sink(port)) { + if (port->vbus_present && tcpm_port_is_sink(port)) tcpm_set_state(port, SNK_ATTACHED, 0); - } else { - tcpm_set_state(port, SRC_TRYWAIT, 0); + else port->max_wait = 0; - } break; case SRC_TRYWAIT: tcpm_set_cc(port, tcpm_rp_cc(port)); @@ -4053,6 +4051,12 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1, if (!tcpm_port_is_sink(port)) tcpm_set_state(port, SNK_TRYWAIT_DEBOUNCE, 0); break; + case SNK_TRY_WAIT_DEBOUNCE_CHECK_VBUS: + if (!tcpm_port_is_sink(port)) + tcpm_set_state(port, SRC_TRYWAIT, PD_T_TRY_CC_DEBOUNCE); + else + tcpm_set_state(port, SNK_TRY_WAIT_DEBOUNCE_CHECK_VBUS, 0); + break; case SNK_TRYWAIT: /* Do nothing, waiting for tCCDebounce */ break; @@ -4139,6 +4143,10 @@ static void _tcpm_pd_vbus_on(struct tcpm_port *port) case SNK_TRYWAIT_DEBOUNCE: /* Do nothing, waiting for Rp */ break; + case SNK_TRY_WAIT_DEBOUNCE_CHECK_VBUS: + if (port->vbus_present && tcpm_port_is_sink(port)) + tcpm_set_state(port, SNK_ATTACHED, 0); + break; case SRC_TRY_WAIT: case SRC_TRY_DEBOUNCE: /* Do nothing, waiting for sink detection */ diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h index 3a805e2ecbc9..63a66dd5d832 100644 --- a/include/linux/usb/pd.h +++ b/include/linux/usb/pd.h @@ -484,6 +484,7 @@ static inline unsigned int rdo_max_power(u32 rdo) #define PD_T_CC_DEBOUNCE 200 /* 100 - 200 ms */ #define PD_T_PD_DEBOUNCE 20 /* 10 - 20 ms */ +#define PD_T_TRY_CC_DEBOUNCE 15 /* 10 - 20 ms */ #define PD_N_CAPS_COUNT (PD_T_NO_RESPONSE / PD_T_SEND_SOURCE_CAP) #define PD_N_HARD_RESET_COUNT 2 -- cgit v1.2.3 From e4a9378083c57a22624cda8b780ff5f5da4de7ef Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Tue, 1 Dec 2020 19:17:33 -0800 Subject: usb: typec: tcpm: Pass down negotiated rev to update retry count nRetryCount was updated from 3 to 2 between PD2.0 and PD3.0 spec. nRetryCount in "Table 6-34 Counter parameters" of the PD 2.0 spec is set to 3, whereas, nRetryCount in "Table 6-59 Counter parameters" is set to 2. Pass down negotiated rev in pd_transmit so that low level chip drivers can update the retry count accordingly before attempting packet transmission. This helps in passing "TEST.PD.PORT.ALL.02" of the "Power Delivery Merged" test suite which was initially failing with "The UUT did not retransmit the message nReryCount times" In fusb302 & tcpci drivers, by default the driver sets the retry count to 3 (Default for PD 2.0). Update this to 2, if the negotiated rev is PD 3.0. In wcove, since the retry count is intentionally set to max, leaving it as is. Reviewed-by: Guenter Roeck Acked-by: Heikki Krogerus Signed-off-by: Badhri Jagan Sridharan Link: https://lore.kernel.org/r/20201202031733.647808-1-badhri@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/fusb302.c | 16 +++++++++++----- drivers/usb/typec/tcpm/tcpci.c | 12 +++++++----- drivers/usb/typec/tcpm/tcpm.c | 2 +- drivers/usb/typec/tcpm/wcove.c | 3 ++- include/linux/usb/tcpm.h | 2 +- 5 files changed, 22 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c index 99562cc65ca6..ebc46b9f776c 100644 --- a/drivers/usb/typec/tcpm/fusb302.c +++ b/drivers/usb/typec/tcpm/fusb302.c @@ -343,12 +343,11 @@ static int fusb302_sw_reset(struct fusb302_chip *chip) return ret; } -static int fusb302_enable_tx_auto_retries(struct fusb302_chip *chip) +static int fusb302_enable_tx_auto_retries(struct fusb302_chip *chip, u8 retry_count) { int ret = 0; - ret = fusb302_i2c_set_bits(chip, FUSB_REG_CONTROL3, - FUSB_REG_CONTROL3_N_RETRIES_3 | + ret = fusb302_i2c_set_bits(chip, FUSB_REG_CONTROL3, retry_count | FUSB_REG_CONTROL3_AUTO_RETRY); return ret; @@ -399,7 +398,7 @@ static int tcpm_init(struct tcpc_dev *dev) ret = fusb302_sw_reset(chip); if (ret < 0) return ret; - ret = fusb302_enable_tx_auto_retries(chip); + ret = fusb302_enable_tx_auto_retries(chip, FUSB_REG_CONTROL3_N_RETRIES_3); if (ret < 0) return ret; ret = fusb302_init_interrupt(chip); @@ -1017,7 +1016,7 @@ static const char * const transmit_type_name[] = { }; static int tcpm_pd_transmit(struct tcpc_dev *dev, enum tcpm_transmit_type type, - const struct pd_message *msg) + const struct pd_message *msg, unsigned int negotiated_rev) { struct fusb302_chip *chip = container_of(dev, struct fusb302_chip, tcpc_dev); @@ -1026,6 +1025,13 @@ static int tcpm_pd_transmit(struct tcpc_dev *dev, enum tcpm_transmit_type type, mutex_lock(&chip->lock); switch (type) { case TCPC_TX_SOP: + /* nRetryCount 3 in P2.0 spec, whereas 2 in PD3.0 spec */ + ret = fusb302_enable_tx_auto_retries(chip, negotiated_rev > PD_REV20 ? + FUSB_REG_CONTROL3_N_RETRIES_2 : + FUSB_REG_CONTROL3_N_RETRIES_3); + if (ret < 0) + fusb302_log(chip, "Cannot update retry count ret=%d", ret); + ret = fusb302_pd_send_message(chip, msg); if (ret < 0) fusb302_log(chip, diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c index 12d983a75510..98a2455f779f 100644 --- a/drivers/usb/typec/tcpm/tcpci.c +++ b/drivers/usb/typec/tcpm/tcpci.c @@ -18,7 +18,8 @@ #include "tcpci.h" -#define PD_RETRY_COUNT 3 +#define PD_RETRY_COUNT_DEFAULT 3 +#define PD_RETRY_COUNT_3_0_OR_HIGHER 2 #define AUTO_DISCHARGE_DEFAULT_THRESHOLD_MV 3500 #define AUTO_DISCHARGE_PD_HEADROOM_MV 850 #define AUTO_DISCHARGE_PPS_HEADROOM_MV 1250 @@ -447,9 +448,8 @@ static int tcpci_set_vbus(struct tcpc_dev *tcpc, bool source, bool sink) return 0; } -static int tcpci_pd_transmit(struct tcpc_dev *tcpc, - enum tcpm_transmit_type type, - const struct pd_message *msg) +static int tcpci_pd_transmit(struct tcpc_dev *tcpc, enum tcpm_transmit_type type, + const struct pd_message *msg, unsigned int negotiated_rev) { struct tcpci *tcpci = tcpc_to_tcpci(tcpc); u16 header = msg ? le16_to_cpu(msg->header) : 0; @@ -497,7 +497,9 @@ static int tcpci_pd_transmit(struct tcpc_dev *tcpc, } } - reg = (PD_RETRY_COUNT << TCPC_TRANSMIT_RETRY_SHIFT) | (type << TCPC_TRANSMIT_TYPE_SHIFT); + /* nRetryCount is 3 in PD2.0 spec where 2 in PD3.0 spec */ + reg = ((negotiated_rev > PD_REV20 ? PD_RETRY_COUNT_3_0_OR_HIGHER : PD_RETRY_COUNT_DEFAULT) + << TCPC_TRANSMIT_RETRY_SHIFT) | (type << TCPC_TRANSMIT_TYPE_SHIFT); ret = regmap_write(tcpci->regmap, TCPC_TRANSMIT, reg); if (ret < 0) return ret; diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 3bbc1f10af49..c73bc3a8356a 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -667,7 +667,7 @@ static int tcpm_pd_transmit(struct tcpm_port *port, tcpm_log(port, "PD TX, type: %#x", type); reinit_completion(&port->tx_complete); - ret = port->tcpc->pd_transmit(port->tcpc, type, msg); + ret = port->tcpc->pd_transmit(port->tcpc, type, msg, port->negotiated_rev); if (ret < 0) return ret; diff --git a/drivers/usb/typec/tcpm/wcove.c b/drivers/usb/typec/tcpm/wcove.c index 9b745f432c91..79ae63950050 100644 --- a/drivers/usb/typec/tcpm/wcove.c +++ b/drivers/usb/typec/tcpm/wcove.c @@ -356,7 +356,8 @@ static int wcove_set_pd_rx(struct tcpc_dev *tcpc, bool on) static int wcove_pd_transmit(struct tcpc_dev *tcpc, enum tcpm_transmit_type type, - const struct pd_message *msg) + const struct pd_message *msg, + unsigned int negotiated_rev) { struct wcove_typec *wcove = tcpc_to_wcove(tcpc); unsigned int info = 0; diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h index e68aaa12886f..efaedd7e8a18 100644 --- a/include/linux/usb/tcpm.h +++ b/include/linux/usb/tcpm.h @@ -121,7 +121,7 @@ struct tcpc_dev { enum typec_cc_status cc); int (*try_role)(struct tcpc_dev *dev, int role); int (*pd_transmit)(struct tcpc_dev *dev, enum tcpm_transmit_type type, - const struct pd_message *msg); + const struct pd_message *msg, unsigned int negotiated_rev); int (*set_bist_data)(struct tcpc_dev *dev, bool on); int (*enable_frs)(struct tcpc_dev *dev, bool enable); void (*frs_sourcing_vbus)(struct tcpc_dev *dev); -- cgit v1.2.3 From 28b43d3d746b89fc112fe681e018b39b43495dad Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Tue, 1 Dec 2020 20:08:38 -0800 Subject: usb: typec: tcpm: Introduce vsafe0v for vbus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TCPM at present lacks the notion of VSAFE0V. There are three vbus threshold levels that are critical to track: a. vSafe5V - VBUS “5 volts” as defined by the USB PD specification. b. vSinkDisconnect - Threshold used for transition from Attached.SNK to Unattached.SNK. c. vSafe0V - VBUS “0 volts” as defined by the USB PD specification. Tracking vSafe0V is crucial for entry into Try.SNK and Attached.SRC and turning vbus back on by the source in response to hard reset. >From "4.5.2.2.8.2 Exiting from AttachWait.SRC State" section in the Type-C spec: "The port shall transition to Attached.SRC when VBUS is at vSafe0V and the SRC.Rd state is detected on exactly one of the CC1 or CC2 pins for at least tCCDebounce." "A DRP that strongly prefers the Sink role may optionally transition to Try.SNK instead of Attached.SRC when VBUS is at vSafe0V and the SRC.Rd state is detected on exactly one of the CC1 or CC2 pins for at least tCCDebounce." >From "7.1.5 Response to Hard Resets" section in the PD spec: "After establishing the vSafe0V voltage condition on VBUS, the Source Shall wait tSrcRecover before re-applying VCONN and restoring VBUS to vSafe5V." vbus_present in the TCPM code tracks vSafe5V(vbus_present is true) and vSinkDisconnect(vbus_present is false). This change adds is_vbus_vsafe0v callback which when set makes TCPM query for vSafe0V voltage level when needed. Since not all TCPC controllers might have the capability to report vSafe0V, TCPM assumes that vSafe0V is same as vSinkDisconnect when is_vbus_vsafe0v callback is not set. This allows TCPM to continue to support controllers which don't have the support for reporting vSafe0V. Introducing vSafe0V helps fix the failure reported at "Step 15. CVS verifies PUT remains in AttachWait.SRC for 500ms" of "TD 4.7.2 Try. SNK DRP Connect DRP Test" of "Universal Serial Bus Type-C (USB Type-C) Functional Test Specification Chapters 4 and 5". Here the compliance tester intentionally maintains vbus at greater than vSafe0V and expects the Product under test to stay in AttachWait.SRC till vbus drops to vSafe0V. Reviewed-by: Guenter Roeck Acked-by: Heikki Krogerus Signed-off-by: Badhri Jagan Sridharan Link: https://lore.kernel.org/r/20201202040840.663578-1-badhri@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 63 +++++++++++++++++++++++++++++++++++-------- include/linux/usb/tcpm.h | 7 +++++ 2 files changed, 59 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 1c64b831bbb5..cedc6cf82d61 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -258,7 +258,19 @@ struct tcpm_port { bool attached; bool connected; enum typec_port_type port_type; + + /* + * Set to true when vbus is greater than VSAFE5V min. + * Set to false when vbus falls below vSinkDisconnect max threshold. + */ bool vbus_present; + + /* + * Set to true when vbus is less than VSAFE0V max. + * Set to false when vbus is greater than VSAFE0V max. + */ + bool vbus_vsafe0v; + bool vbus_never_low; bool vbus_source; bool vbus_charge; @@ -3093,7 +3105,7 @@ static void run_state_machine(struct tcpm_port *port) else if (tcpm_port_is_audio(port)) tcpm_set_state(port, AUDIO_ACC_ATTACHED, PD_T_CC_DEBOUNCE); - else if (tcpm_port_is_source(port)) + else if (tcpm_port_is_source(port) && port->vbus_vsafe0v) tcpm_set_state(port, tcpm_try_snk(port) ? SNK_TRY : SRC_ATTACHED, @@ -4096,6 +4108,12 @@ static void _tcpm_pd_vbus_on(struct tcpm_port *port) { tcpm_log_force(port, "VBUS on"); port->vbus_present = true; + /* + * When vbus_present is true i.e. Voltage at VBUS is greater than VSAFE5V implicitly + * states that vbus is not at VSAFE0V, hence clear the vbus_vsafe0v flag here. + */ + port->vbus_vsafe0v = false; + switch (port->state) { case SNK_TRANSITION_SINK_VBUS: port->explicit_contract = true; @@ -4185,16 +4203,8 @@ static void _tcpm_pd_vbus_off(struct tcpm_port *port) case SNK_HARD_RESET_SINK_OFF: tcpm_set_state(port, SNK_HARD_RESET_WAIT_VBUS, 0); break; - case SRC_HARD_RESET_VBUS_OFF: - /* - * After establishing the vSafe0V voltage condition on VBUS, the Source Shall wait - * tSrcRecover before re-applying VCONN and restoring VBUS to vSafe5V. - */ - tcpm_set_state(port, SRC_HARD_RESET_VBUS_ON, PD_T_SRC_RECOVER); - break; case HARD_RESET_SEND: break; - case SNK_TRY: /* Do nothing, waiting for timeout */ break; @@ -4265,6 +4275,28 @@ static void _tcpm_pd_vbus_off(struct tcpm_port *port) } } +static void _tcpm_pd_vbus_vsafe0v(struct tcpm_port *port) +{ + tcpm_log_force(port, "VBUS VSAFE0V"); + port->vbus_vsafe0v = true; + switch (port->state) { + case SRC_HARD_RESET_VBUS_OFF: + /* + * After establishing the vSafe0V voltage condition on VBUS, the Source Shall wait + * tSrcRecover before re-applying VCONN and restoring VBUS to vSafe5V. + */ + tcpm_set_state(port, SRC_HARD_RESET_VBUS_ON, PD_T_SRC_RECOVER); + break; + case SRC_ATTACH_WAIT: + if (tcpm_port_is_source(port)) + tcpm_set_state(port, tcpm_try_snk(port) ? SNK_TRY : SRC_ATTACHED, + PD_T_CC_DEBOUNCE); + break; + default: + break; + } +} + static void _tcpm_pd_hard_reset(struct tcpm_port *port) { tcpm_log_force(port, "Received hard reset"); @@ -4300,10 +4332,19 @@ static void tcpm_pd_event_handler(struct kthread_work *work) bool vbus; vbus = port->tcpc->get_vbus(port->tcpc); - if (vbus) + if (vbus) { _tcpm_pd_vbus_on(port); - else + } else { _tcpm_pd_vbus_off(port); + /* + * When TCPC does not support detecting vsafe0v voltage level, + * treat vbus absent as vsafe0v. Else invoke is_vbus_vsafe0v + * to see if vbus has discharge to VSAFE0V. + */ + if (!port->tcpc->is_vbus_vsafe0v || + port->tcpc->is_vbus_vsafe0v(port->tcpc)) + _tcpm_pd_vbus_vsafe0v(port); + } } if (events & TCPM_CC_EVENT) { enum typec_cc_status cc1, cc2; diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h index efaedd7e8a18..f4a18427f5c4 100644 --- a/include/linux/usb/tcpm.h +++ b/include/linux/usb/tcpm.h @@ -98,6 +98,12 @@ enum tcpm_transmit_type { * will be turned on. requested_vbus_voltage is set to 0 when vbus * is going to disappear knowingly i.e. during PR_SWAP and * HARD_RESET etc. + * @is_vbus_vsafe0v: + * Optional; TCPCI spec based TCPC implementations are expected to + * detect VSAFE0V voltage level at vbus. When detection of VSAFE0V + * is supported by TCPC, set this callback for TCPM to query + * whether vbus is at VSAFE0V when needed. + * Returns true when vbus is at VSAFE0V, false otherwise. */ struct tcpc_dev { struct fwnode_handle *fwnode; @@ -128,6 +134,7 @@ struct tcpc_dev { int (*enable_auto_vbus_discharge)(struct tcpc_dev *dev, bool enable); int (*set_auto_vbus_discharge_threshold)(struct tcpc_dev *dev, enum typec_pwr_opmode mode, bool pps_active, u32 requested_vbus_voltage); + bool (*is_vbus_vsafe0v)(struct tcpc_dev *dev); }; struct tcpm_port; -- cgit v1.2.3 From 8010622c86ca5bb44bc98492f5968726fc7c7a21 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 9 Dec 2020 16:26:39 +0100 Subject: USB: UAS: introduce a quirk to set no_write_same UAS does not share the pessimistic assumption storage is making that devices cannot deal with WRITE_SAME. A few devices supported by UAS, are reported to not deal well with WRITE_SAME. Those need a quirk. Add it to the device that needs it. Reported-by: David C. Partridge Signed-off-by: Oliver Neukum Cc: stable Link: https://lore.kernel.org/r/20201209152639.9195-1-oneukum@suse.com Signed-off-by: Greg Kroah-Hartman --- Documentation/admin-guide/kernel-parameters.txt | 1 + drivers/usb/storage/uas.c | 3 +++ drivers/usb/storage/unusual_uas.h | 7 +++++-- drivers/usb/storage/usb.c | 3 +++ include/linux/usb_usual.h | 2 ++ 5 files changed, 14 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 44fde25bb221..f6a1513dfb76 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -5663,6 +5663,7 @@ device); j = NO_REPORT_LUNS (don't use report luns command, uas only); + k = NO_SAME (do not use WRITE_SAME, uas only) l = NOT_LOCKABLE (don't try to lock and unlock ejectable media, not on uas); m = MAX_SECTORS_64 (don't transfer more diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 56422c4b4ff3..bef89c6bd1d7 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -868,6 +868,9 @@ static int uas_slave_configure(struct scsi_device *sdev) if (devinfo->flags & US_FL_NO_READ_CAPACITY_16) sdev->no_read_capacity_16 = 1; + /* Some disks cannot handle WRITE_SAME */ + if (devinfo->flags & US_FL_NO_SAME) + sdev->no_write_same = 1; /* * Some disks return the total number of blocks in response * to READ CAPACITY rather than the highest block number. diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h index 711ab240058c..870e9cf3d5dc 100644 --- a/drivers/usb/storage/unusual_uas.h +++ b/drivers/usb/storage/unusual_uas.h @@ -35,12 +35,15 @@ UNUSUAL_DEV(0x054c, 0x087d, 0x0000, 0x9999, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NO_REPORT_OPCODES), -/* Reported-by: Julian Groß */ +/* + * Initially Reported-by: Julian Groß + * Further reports David C. Partridge + */ UNUSUAL_DEV(0x059f, 0x105f, 0x0000, 0x9999, "LaCie", "2Big Quadra USB3", USB_SC_DEVICE, USB_PR_DEVICE, NULL, - US_FL_NO_REPORT_OPCODES), + US_FL_NO_REPORT_OPCODES | US_FL_NO_SAME), /* * Apricorn USB3 dongle sometimes returns "USBSUSBSUSBS" in response to SCSI diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 94a64729dc27..90aa9c12ffac 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -541,6 +541,9 @@ void usb_stor_adjust_quirks(struct usb_device *udev, unsigned long *fflags) case 'j': f |= US_FL_NO_REPORT_LUNS; break; + case 'k': + f |= US_FL_NO_SAME; + break; case 'l': f |= US_FL_NOT_LOCKABLE; break; diff --git a/include/linux/usb_usual.h b/include/linux/usb_usual.h index 4a19ac3f24d0..6b03fdd69d27 100644 --- a/include/linux/usb_usual.h +++ b/include/linux/usb_usual.h @@ -84,6 +84,8 @@ /* Cannot handle REPORT_LUNS */ \ US_FLAG(ALWAYS_SYNC, 0x20000000) \ /* lies about caching, so always sync */ \ + US_FLAG(NO_SAME, 0x40000000) \ + /* Cannot handle WRITE_SAME */ \ #define US_FLAG(name, value) US_FL_##name = value , enum { US_DO_ALL_FLAGS }; -- cgit v1.2.3 From fe79d5de77204dd946cfad76a9bec23354b1a500 Mon Sep 17 00:00:00 2001 From: Kyle Tso Date: Thu, 10 Dec 2020 17:05:20 +0100 Subject: USB: typec: tcpm: Add a 30ms room for tPSSourceOn in PR_SWAP TCPM state machine needs 20-25ms to enter the ErrorRecovery state after tPSSourceOn timer timeouts. Change the timer from max 480ms to 450ms to ensure that the timer complies with the Spec. In order to keep the flexibility for other usecases using tPSSourceOn, add another timer only for PR_SWAP. Cc: Guenter Roeck Cc: Heikki Krogerus Cc: Badhri Jagan Sridharan Reviewed-by: Guenter Roeck Acked-by: Heikki Krogerus Signed-off-by: Kyle Tso Signed-off-by: Will McVicker Signed-off-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20201210160521.3417426-5-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 2 +- include/linux/usb/pd.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 3f9edc8a9a57..36b6ce85093f 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -3738,7 +3738,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, ERROR_RECOVERY, 0); break; } - tcpm_set_state(port, ERROR_RECOVERY, PD_T_PS_SOURCE_ON); + tcpm_set_state(port, ERROR_RECOVERY, PD_T_PS_SOURCE_ON_PRS); break; case PR_SWAP_SRC_SNK_SINK_ON: /* Set the vbus disconnect threshold for implicit contract */ diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h index 63a66dd5d832..bb9a782e1411 100644 --- a/include/linux/usb/pd.h +++ b/include/linux/usb/pd.h @@ -466,6 +466,7 @@ static inline unsigned int rdo_max_power(u32 rdo) #define PD_T_DRP_SRC 30 #define PD_T_PS_SOURCE_OFF 920 #define PD_T_PS_SOURCE_ON 480 +#define PD_T_PS_SOURCE_ON_PRS 450 /* 390 - 480ms */ #define PD_T_PS_HARD_RESET 30 #define PD_T_SRC_RECOVER 760 #define PD_T_SRC_RECOVER_MAX 1000 -- cgit v1.2.3