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 | |
15 | static int connector_bind(struct device *dev, struct device *connector, void *data) |
16 | { |
17 | int ret; |
18 | |
19 | ret = sysfs_create_link(kobj: &dev->kobj, target: &connector->kobj, name: "connector" ); |
20 | if (ret) |
21 | return ret; |
22 | |
23 | ret = sysfs_create_link(kobj: &connector->kobj, target: &dev->kobj, name: dev_name(dev)); |
24 | if (ret) |
25 | sysfs_remove_link(kobj: &dev->kobj, name: "connector" ); |
26 | |
27 | return ret; |
28 | } |
29 | |
30 | static void connector_unbind(struct device *dev, struct device *connector, void *data) |
31 | { |
32 | sysfs_remove_link(kobj: &connector->kobj, name: dev_name(dev)); |
33 | sysfs_remove_link(kobj: &dev->kobj, name: "connector" ); |
34 | } |
35 | |
36 | static const struct component_ops connector_ops = { |
37 | .bind = connector_bind, |
38 | .unbind = connector_unbind, |
39 | }; |
40 | |
41 | static 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(lock: &tb->lock); |
62 | |
63 | return sysfs_emit(buf, fmt: "%s\n" , link); |
64 | } |
65 | static DEVICE_ATTR_RO(link); |
66 | |
67 | static struct attribute *common_attrs[] = { |
68 | &dev_attr_link.attr, |
69 | NULL |
70 | }; |
71 | |
72 | static const struct attribute_group common_group = { |
73 | .attrs = common_attrs, |
74 | }; |
75 | |
76 | static 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, add: false); |
92 | if (ret) { |
93 | usb4_port_router_online(port); |
94 | tb_acpi_power_off_retimers(port); |
95 | } |
96 | |
97 | return ret; |
98 | } |
99 | |
100 | static 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 | static ssize_t offline_show(struct device *dev, |
109 | struct device_attribute *attr, char *buf) |
110 | { |
111 | struct usb4_port *usb4 = tb_to_usb4_port_device(dev); |
112 | |
113 | return sysfs_emit(buf, fmt: "%d\n" , usb4->offline); |
114 | } |
115 | |
116 | static ssize_t offline_store(struct device *dev, |
117 | struct device_attribute *attr, const char *buf, size_t count) |
118 | { |
119 | struct usb4_port *usb4 = tb_to_usb4_port_device(dev); |
120 | struct tb_port *port = usb4->port; |
121 | struct tb *tb = port->sw->tb; |
122 | bool val; |
123 | int ret; |
124 | |
125 | ret = kstrtobool(s: buf, res: &val); |
126 | if (ret) |
127 | return ret; |
128 | |
129 | pm_runtime_get_sync(dev: &usb4->dev); |
130 | |
131 | if (mutex_lock_interruptible(&tb->lock)) { |
132 | ret = -ERESTARTSYS; |
133 | goto out_rpm; |
134 | } |
135 | |
136 | if (val == usb4->offline) |
137 | goto out_unlock; |
138 | |
139 | /* Offline mode works only for ports that are not connected */ |
140 | if (tb_port_has_remote(port)) { |
141 | ret = -EBUSY; |
142 | goto out_unlock; |
143 | } |
144 | |
145 | if (val) { |
146 | ret = usb4_port_offline(usb4); |
147 | if (ret) |
148 | goto out_unlock; |
149 | } else { |
150 | usb4_port_online(usb4); |
151 | tb_retimer_remove_all(port); |
152 | } |
153 | |
154 | usb4->offline = val; |
155 | tb_port_dbg(port, "%s offline mode\n" , val ? "enter" : "exit" ); |
156 | |
157 | out_unlock: |
158 | mutex_unlock(lock: &tb->lock); |
159 | out_rpm: |
160 | pm_runtime_mark_last_busy(dev: &usb4->dev); |
161 | pm_runtime_put_autosuspend(dev: &usb4->dev); |
162 | |
163 | return ret ? ret : count; |
164 | } |
165 | static DEVICE_ATTR_RW(offline); |
166 | |
167 | static ssize_t rescan_store(struct device *dev, |
168 | struct device_attribute *attr, const char *buf, size_t count) |
169 | { |
170 | struct usb4_port *usb4 = tb_to_usb4_port_device(dev); |
171 | struct tb_port *port = usb4->port; |
172 | struct tb *tb = port->sw->tb; |
173 | bool val; |
174 | int ret; |
175 | |
176 | ret = kstrtobool(s: buf, res: &val); |
177 | if (ret) |
178 | return ret; |
179 | |
180 | if (!val) |
181 | return count; |
182 | |
183 | pm_runtime_get_sync(dev: &usb4->dev); |
184 | |
185 | if (mutex_lock_interruptible(&tb->lock)) { |
186 | ret = -ERESTARTSYS; |
187 | goto out_rpm; |
188 | } |
189 | |
190 | /* Must be in offline mode already */ |
191 | if (!usb4->offline) { |
192 | ret = -EINVAL; |
193 | goto out_unlock; |
194 | } |
195 | |
196 | tb_retimer_remove_all(port); |
197 | ret = tb_retimer_scan(port, add: true); |
198 | |
199 | out_unlock: |
200 | mutex_unlock(lock: &tb->lock); |
201 | out_rpm: |
202 | pm_runtime_mark_last_busy(dev: &usb4->dev); |
203 | pm_runtime_put_autosuspend(dev: &usb4->dev); |
204 | |
205 | return ret ? ret : count; |
206 | } |
207 | static DEVICE_ATTR_WO(rescan); |
208 | |
209 | static struct attribute *service_attrs[] = { |
210 | &dev_attr_offline.attr, |
211 | &dev_attr_rescan.attr, |
212 | NULL |
213 | }; |
214 | |
215 | static umode_t service_attr_is_visible(struct kobject *kobj, |
216 | struct attribute *attr, int n) |
217 | { |
218 | struct device *dev = kobj_to_dev(kobj); |
219 | struct usb4_port *usb4 = tb_to_usb4_port_device(dev); |
220 | |
221 | /* |
222 | * Always need some platform help to cycle the modes so that |
223 | * retimers can be accessed through the sideband. |
224 | */ |
225 | return usb4->can_offline ? attr->mode : 0; |
226 | } |
227 | |
228 | static const struct attribute_group service_group = { |
229 | .attrs = service_attrs, |
230 | .is_visible = service_attr_is_visible, |
231 | }; |
232 | |
233 | static const struct attribute_group *usb4_port_device_groups[] = { |
234 | &common_group, |
235 | &service_group, |
236 | NULL |
237 | }; |
238 | |
239 | static void usb4_port_device_release(struct device *dev) |
240 | { |
241 | struct usb4_port *usb4 = container_of(dev, struct usb4_port, dev); |
242 | |
243 | kfree(objp: usb4); |
244 | } |
245 | |
246 | const struct device_type usb4_port_device_type = { |
247 | .name = "usb4_port" , |
248 | .groups = usb4_port_device_groups, |
249 | .release = usb4_port_device_release, |
250 | }; |
251 | |
252 | /** |
253 | * usb4_port_device_add() - Add USB4 port device |
254 | * @port: Lane 0 adapter port to add the USB4 port |
255 | * |
256 | * Creates and registers a USB4 port device for @port. Returns the new |
257 | * USB4 port device pointer or ERR_PTR() in case of error. |
258 | */ |
259 | struct usb4_port *usb4_port_device_add(struct tb_port *port) |
260 | { |
261 | struct usb4_port *usb4; |
262 | int ret; |
263 | |
264 | usb4 = kzalloc(size: sizeof(*usb4), GFP_KERNEL); |
265 | if (!usb4) |
266 | return ERR_PTR(error: -ENOMEM); |
267 | |
268 | usb4->port = port; |
269 | usb4->dev.type = &usb4_port_device_type; |
270 | usb4->dev.parent = &port->sw->dev; |
271 | dev_set_name(dev: &usb4->dev, name: "usb4_port%d" , port->port); |
272 | |
273 | ret = device_register(dev: &usb4->dev); |
274 | if (ret) { |
275 | put_device(dev: &usb4->dev); |
276 | return ERR_PTR(error: ret); |
277 | } |
278 | |
279 | if (dev_fwnode(&usb4->dev)) { |
280 | ret = component_add(&usb4->dev, &connector_ops); |
281 | if (ret) { |
282 | dev_err(&usb4->dev, "failed to add component\n" ); |
283 | device_unregister(dev: &usb4->dev); |
284 | } |
285 | } |
286 | |
287 | if (!tb_is_upstream_port(port)) |
288 | device_set_wakeup_capable(dev: &usb4->dev, capable: true); |
289 | |
290 | pm_runtime_no_callbacks(dev: &usb4->dev); |
291 | pm_runtime_set_active(dev: &usb4->dev); |
292 | pm_runtime_enable(dev: &usb4->dev); |
293 | pm_runtime_set_autosuspend_delay(dev: &usb4->dev, TB_AUTOSUSPEND_DELAY); |
294 | pm_runtime_mark_last_busy(dev: &usb4->dev); |
295 | pm_runtime_use_autosuspend(dev: &usb4->dev); |
296 | |
297 | return usb4; |
298 | } |
299 | |
300 | /** |
301 | * usb4_port_device_remove() - Removes USB4 port device |
302 | * @usb4: USB4 port device |
303 | * |
304 | * Unregisters the USB4 port device from the system. The device will be |
305 | * released when the last reference is dropped. |
306 | */ |
307 | void usb4_port_device_remove(struct usb4_port *usb4) |
308 | { |
309 | if (dev_fwnode(&usb4->dev)) |
310 | component_del(&usb4->dev, &connector_ops); |
311 | device_unregister(dev: &usb4->dev); |
312 | } |
313 | |
314 | /** |
315 | * usb4_port_device_resume() - Resumes USB4 port device |
316 | * @usb4: USB4 port device |
317 | * |
318 | * Used to resume USB4 port device after sleep state. |
319 | */ |
320 | int usb4_port_device_resume(struct usb4_port *usb4) |
321 | { |
322 | return usb4->offline ? usb4_port_offline(usb4) : 0; |
323 | } |
324 | |