File Index
Files and directories
Sign in
FILE

usb4_port.c

File preview with syntax highlighting.

linux-6.19.14/drivers/thunderbolt/usb4_port.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * USB4 port device
  4 *
  5 * Copyright (C) 2021, Intel Corporation
  6 * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
  7 */
  8
  9#include <linux/pm_runtime.h>
 10#include <linux/component.h>
 11#include <linux/property.h>
 12
 13#include "tb.h"
 14
 15static int connector_bind(struct device *dev, struct device *connector, void *data)
 16{
 17	int ret;
 18
 19	ret = sysfs_create_link(&dev->kobj, &connector->kobj, "connector");
 20	if (ret)
 21		return ret;
 22
 23	ret = sysfs_create_link(&connector->kobj, &dev->kobj, dev_name(dev));
 24	if (ret)
 25		sysfs_remove_link(&dev->kobj, "connector");
 26
 27	return ret;
 28}
 29
 30static void connector_unbind(struct device *dev, struct device *connector, void *data)
 31{
 32	sysfs_remove_link(&connector->kobj, dev_name(dev));
 33	sysfs_remove_link(&dev->kobj, "connector");
 34}
 35
 36static const struct component_ops connector_ops = {
 37	.bind = connector_bind,
 38	.unbind = connector_unbind,
 39};
 40
 41static ssize_t link_show(struct device *dev, struct device_attribute *attr,
 42			 char *buf)
 43{
 44	struct usb4_port *usb4 = tb_to_usb4_port_device(dev);
 45	struct tb_port *port = usb4->port;
 46	struct tb *tb = port->sw->tb;
 47	const char *link;
 48
 49	if (mutex_lock_interruptible(&tb->lock))
 50		return -ERESTARTSYS;
 51
 52	if (tb_is_upstream_port(port))
 53		link = port->sw->link_usb4 ? "usb4" : "tbt";
 54	else if (tb_port_has_remote(port))
 55		link = port->remote->sw->link_usb4 ? "usb4" : "tbt";
 56	else if (port->xdomain)
 57		link = port->xdomain->link_usb4 ? "usb4" : "tbt";
 58	else
 59		link = "none";
 60
 61	mutex_unlock(&tb->lock);
 62
 63	return sysfs_emit(buf, "%s\n", link);
 64}
 65static DEVICE_ATTR_RO(link);
 66
 67static struct attribute *common_attrs[] = {
 68	&dev_attr_link.attr,
 69	NULL
 70};
 71
 72static const struct attribute_group common_group = {
 73	.attrs = common_attrs,
 74};
 75
 76static int usb4_port_offline(struct usb4_port *usb4)
 77{
 78	struct tb_port *port = usb4->port;
 79	int ret;
 80
 81	ret = tb_acpi_power_on_retimers(port);
 82	if (ret)
 83		return ret;
 84
 85	ret = usb4_port_router_offline(port);
 86	if (ret) {
 87		tb_acpi_power_off_retimers(port);
 88		return ret;
 89	}
 90
 91	ret = tb_retimer_scan(port, false);
 92	if (ret) {
 93		usb4_port_router_online(port);
 94		tb_acpi_power_off_retimers(port);
 95	}
 96
 97	return ret;
 98}
 99
100static void usb4_port_online(struct usb4_port *usb4)
101{
102	struct tb_port *port = usb4->port;
103
104	usb4_port_router_online(port);
105	tb_acpi_power_off_retimers(port);
106}
107
108/**
109 * usb4_usb3_port_match() - Matches USB4 port device with USB 3.x port device
110 * @usb4_port_dev: USB4 port device
111 * @usb3_port_fwnode: USB 3.x port firmware node
112 *
113 * Checks if USB 3.x port @usb3_port_fwnode is tunneled through USB4 port @usb4_port_dev.
114 * Returns true if match is found, false otherwise.
115 *
116 * Function is designed to be used with component framework (component_match_add).
117 */
118bool usb4_usb3_port_match(struct device *usb4_port_dev,
119			  const struct fwnode_handle *usb3_port_fwnode)
120{
121	struct fwnode_handle *nhi_fwnode __free(fwnode_handle) = NULL;
122	struct usb4_port *usb4;
123	struct tb_switch *sw;
124	struct tb_nhi *nhi;
125	u8 usb4_port_num;
126	struct tb *tb;
127
128	usb4 = tb_to_usb4_port_device(usb4_port_dev);
129	if (!usb4)
130		return false;
131
132	sw = usb4->port->sw;
133	tb = sw->tb;
134	nhi = tb->nhi;
135
136	nhi_fwnode = fwnode_find_reference(usb3_port_fwnode, "usb4-host-interface", 0);
137	if (IS_ERR(nhi_fwnode))
138		return false;
139
140	/* Check if USB3 fwnode references same NHI where USB4 port resides */
141	if (!device_match_fwnode(&nhi->pdev->dev, nhi_fwnode))
142		return false;
143
144	if (fwnode_property_read_u8(usb3_port_fwnode, "usb4-port-number", &usb4_port_num))
145		return false;
146
147	return usb4_port_index(sw, usb4->port) == usb4_port_num;
148}
149EXPORT_SYMBOL_GPL(usb4_usb3_port_match);
150
151static ssize_t offline_show(struct device *dev,
152	struct device_attribute *attr, char *buf)
153{
154	struct usb4_port *usb4 = tb_to_usb4_port_device(dev);
155
156	return sysfs_emit(buf, "%d\n", usb4->offline);
157}
158
159static ssize_t offline_store(struct device *dev,
160	struct device_attribute *attr, const char *buf, size_t count)
161{
162	struct usb4_port *usb4 = tb_to_usb4_port_device(dev);
163	struct tb_port *port = usb4->port;
164	struct tb *tb = port->sw->tb;
165	bool val;
166	int ret;
167
168	ret = kstrtobool(buf, &val);
169	if (ret)
170		return ret;
171
172	pm_runtime_get_sync(&usb4->dev);
173
174	if (mutex_lock_interruptible(&tb->lock)) {
175		ret = -ERESTARTSYS;
176		goto out_rpm;
177	}
178
179	if (val == usb4->offline)
180		goto out_unlock;
181
182	/* Offline mode works only for ports that are not connected */
183	if (tb_port_has_remote(port)) {
184		ret = -EBUSY;
185		goto out_unlock;
186	}
187
188	if (val) {
189		ret = usb4_port_offline(usb4);
190		if (ret)
191			goto out_unlock;
192	} else {
193		usb4_port_online(usb4);
194		tb_retimer_remove_all(port);
195	}
196
197	usb4->offline = val;
198	tb_port_dbg(port, "%s offline mode\n", val ? "enter" : "exit");
199
200out_unlock:
201	mutex_unlock(&tb->lock);
202out_rpm:
203	pm_runtime_mark_last_busy(&usb4->dev);
204	pm_runtime_put_autosuspend(&usb4->dev);
205
206	return ret ? ret : count;
207}
208static DEVICE_ATTR_RW(offline);
209
210static ssize_t rescan_store(struct device *dev,
211	struct device_attribute *attr, const char *buf, size_t count)
212{
213	struct usb4_port *usb4 = tb_to_usb4_port_device(dev);
214	struct tb_port *port = usb4->port;
215	struct tb *tb = port->sw->tb;
216	bool val;
217	int ret;
218
219	ret = kstrtobool(buf, &val);
220	if (ret)
221		return ret;
222
223	if (!val)
224		return count;
225
226	pm_runtime_get_sync(&usb4->dev);
227
228	if (mutex_lock_interruptible(&tb->lock)) {
229		ret = -ERESTARTSYS;
230		goto out_rpm;
231	}
232
233	/* Must be in offline mode already */
234	if (!usb4->offline) {
235		ret = -EINVAL;
236		goto out_unlock;
237	}
238
239	tb_retimer_remove_all(port);
240	ret = tb_retimer_scan(port, true);
241
242out_unlock:
243	mutex_unlock(&tb->lock);
244out_rpm:
245	pm_runtime_mark_last_busy(&usb4->dev);
246	pm_runtime_put_autosuspend(&usb4->dev);
247
248	return ret ? ret : count;
249}
250static DEVICE_ATTR_WO(rescan);
251
252static struct attribute *service_attrs[] = {
253	&dev_attr_offline.attr,
254	&dev_attr_rescan.attr,
255	NULL
256};
257
258static umode_t service_attr_is_visible(struct kobject *kobj,
259				       struct attribute *attr, int n)
260{
261	struct device *dev = kobj_to_dev(kobj);
262	struct usb4_port *usb4 = tb_to_usb4_port_device(dev);
263
264	/*
265	 * Always need some platform help to cycle the modes so that
266	 * retimers can be accessed through the sideband.
267	 */
268	return usb4->can_offline ? attr->mode : 0;
269}
270
271static const struct attribute_group service_group = {
272	.attrs = service_attrs,
273	.is_visible = service_attr_is_visible,
274};
275
276static const struct attribute_group *usb4_port_device_groups[] = {
277	&common_group,
278	&service_group,
279	NULL
280};
281
282static void usb4_port_device_release(struct device *dev)
283{
284	struct usb4_port *usb4 = container_of(dev, struct usb4_port, dev);
285
286	kfree(usb4);
287}
288
289const struct device_type usb4_port_device_type = {
290	.name = "usb4_port",
291	.groups = usb4_port_device_groups,
292	.release = usb4_port_device_release,
293};
294
295/**
296 * usb4_port_device_add() - Add USB4 port device
297 * @port: Lane 0 adapter port to add the USB4 port
298 *
299 * Creates and registers a USB4 port device for @port.
300 *
301 * Return: Pointer to &struct usb4_port or ERR_PTR() in case of an error.
302 */
303struct usb4_port *usb4_port_device_add(struct tb_port *port)
304{
305	struct usb4_port *usb4;
306	int ret;
307
308	usb4 = kzalloc(sizeof(*usb4), GFP_KERNEL);
309	if (!usb4)
310		return ERR_PTR(-ENOMEM);
311
312	usb4->port = port;
313	usb4->dev.type = &usb4_port_device_type;
314	usb4->dev.parent = &port->sw->dev;
315	dev_set_name(&usb4->dev, "usb4_port%d", port->port);
316
317	ret = device_register(&usb4->dev);
318	if (ret) {
319		put_device(&usb4->dev);
320		return ERR_PTR(ret);
321	}
322
323	ret = component_add(&usb4->dev, &connector_ops);
324	if (ret) {
325		dev_err(&usb4->dev, "failed to add component\n");
326		device_unregister(&usb4->dev);
327	}
328
329	if (!tb_is_upstream_port(port))
330		device_set_wakeup_capable(&usb4->dev, true);
331
332	pm_runtime_no_callbacks(&usb4->dev);
333	pm_runtime_set_active(&usb4->dev);
334	pm_runtime_enable(&usb4->dev);
335	pm_runtime_set_autosuspend_delay(&usb4->dev, TB_AUTOSUSPEND_DELAY);
336	pm_runtime_mark_last_busy(&usb4->dev);
337	pm_runtime_use_autosuspend(&usb4->dev);
338
339	return usb4;
340}
341
342/**
343 * usb4_port_device_remove() - Removes USB4 port device
344 * @usb4: USB4 port device
345 *
346 * Unregisters the USB4 port device from the system. The device will be
347 * released when the last reference is dropped.
348 */
349void usb4_port_device_remove(struct usb4_port *usb4)
350{
351	component_del(&usb4->dev, &connector_ops);
352	device_unregister(&usb4->dev);
353}
354
355/**
356 * usb4_port_device_resume() - Resumes USB4 port device
357 * @usb4: USB4 port device
358 *
359 * Used to resume USB4 port device after sleep state.
360 *
361 * Return: %0 on success, negative errno otherwise.
362 */
363int usb4_port_device_resume(struct usb4_port *usb4)
364{
365	return usb4->offline ? usb4_port_offline(usb4) : 0;
366}
~/ Read-only file preview.