Commit | Line | Data |
---|---|---|
cae5f515 MW |
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 | ||
cae5f515 MW |
9 | #include <linux/pm_runtime.h> |
10 | ||
11 | #include "tb.h" | |
12 | ||
13 | static ssize_t link_show(struct device *dev, struct device_attribute *attr, | |
14 | char *buf) | |
15 | { | |
16 | struct usb4_port *usb4 = tb_to_usb4_port_device(dev); | |
17 | struct tb_port *port = usb4->port; | |
18 | struct tb *tb = port->sw->tb; | |
19 | const char *link; | |
20 | ||
21 | if (mutex_lock_interruptible(&tb->lock)) | |
22 | return -ERESTARTSYS; | |
23 | ||
24 | if (tb_is_upstream_port(port)) | |
25 | link = port->sw->link_usb4 ? "usb4" : "tbt"; | |
26 | else if (tb_port_has_remote(port)) | |
27 | link = port->remote->sw->link_usb4 ? "usb4" : "tbt"; | |
28 | else | |
29 | link = "none"; | |
30 | ||
31 | mutex_unlock(&tb->lock); | |
32 | ||
33 | return sysfs_emit(buf, "%s\n", link); | |
34 | } | |
35 | static DEVICE_ATTR_RO(link); | |
36 | ||
37 | static struct attribute *common_attrs[] = { | |
38 | &dev_attr_link.attr, | |
39 | NULL | |
40 | }; | |
41 | ||
42 | static const struct attribute_group common_group = { | |
43 | .attrs = common_attrs, | |
44 | }; | |
45 | ||
3fb10ea4 RM |
46 | static int usb4_port_offline(struct usb4_port *usb4) |
47 | { | |
48 | struct tb_port *port = usb4->port; | |
49 | int ret; | |
50 | ||
51 | ret = tb_acpi_power_on_retimers(port); | |
52 | if (ret) | |
53 | return ret; | |
54 | ||
55 | ret = usb4_port_router_offline(port); | |
56 | if (ret) { | |
57 | tb_acpi_power_off_retimers(port); | |
58 | return ret; | |
59 | } | |
60 | ||
61 | ret = tb_retimer_scan(port, false); | |
62 | if (ret) { | |
63 | usb4_port_router_online(port); | |
64 | tb_acpi_power_off_retimers(port); | |
65 | } | |
66 | ||
67 | return ret; | |
68 | } | |
69 | ||
70 | static void usb4_port_online(struct usb4_port *usb4) | |
71 | { | |
72 | struct tb_port *port = usb4->port; | |
73 | ||
74 | usb4_port_router_online(port); | |
75 | tb_acpi_power_off_retimers(port); | |
76 | } | |
77 | ||
78 | static ssize_t offline_show(struct device *dev, | |
79 | struct device_attribute *attr, char *buf) | |
80 | { | |
81 | struct usb4_port *usb4 = tb_to_usb4_port_device(dev); | |
82 | ||
83 | return sysfs_emit(buf, "%d\n", usb4->offline); | |
84 | } | |
85 | ||
86 | static ssize_t offline_store(struct device *dev, | |
87 | struct device_attribute *attr, const char *buf, size_t count) | |
88 | { | |
89 | struct usb4_port *usb4 = tb_to_usb4_port_device(dev); | |
90 | struct tb_port *port = usb4->port; | |
91 | struct tb *tb = port->sw->tb; | |
92 | bool val; | |
93 | int ret; | |
94 | ||
95 | ret = kstrtobool(buf, &val); | |
96 | if (ret) | |
97 | return ret; | |
98 | ||
99 | pm_runtime_get_sync(&usb4->dev); | |
100 | ||
101 | if (mutex_lock_interruptible(&tb->lock)) { | |
102 | ret = -ERESTARTSYS; | |
103 | goto out_rpm; | |
104 | } | |
105 | ||
106 | if (val == usb4->offline) | |
107 | goto out_unlock; | |
108 | ||
109 | /* Offline mode works only for ports that are not connected */ | |
110 | if (tb_port_has_remote(port)) { | |
111 | ret = -EBUSY; | |
112 | goto out_unlock; | |
113 | } | |
114 | ||
115 | if (val) { | |
116 | ret = usb4_port_offline(usb4); | |
117 | if (ret) | |
118 | goto out_unlock; | |
119 | } else { | |
120 | usb4_port_online(usb4); | |
121 | tb_retimer_remove_all(port); | |
122 | } | |
123 | ||
124 | usb4->offline = val; | |
125 | tb_port_dbg(port, "%s offline mode\n", val ? "enter" : "exit"); | |
126 | ||
127 | out_unlock: | |
128 | mutex_unlock(&tb->lock); | |
129 | out_rpm: | |
130 | pm_runtime_mark_last_busy(&usb4->dev); | |
131 | pm_runtime_put_autosuspend(&usb4->dev); | |
132 | ||
133 | return ret ? ret : count; | |
134 | } | |
135 | static DEVICE_ATTR_RW(offline); | |
136 | ||
137 | static ssize_t rescan_store(struct device *dev, | |
138 | struct device_attribute *attr, const char *buf, size_t count) | |
139 | { | |
140 | struct usb4_port *usb4 = tb_to_usb4_port_device(dev); | |
141 | struct tb_port *port = usb4->port; | |
142 | struct tb *tb = port->sw->tb; | |
143 | bool val; | |
144 | int ret; | |
145 | ||
146 | ret = kstrtobool(buf, &val); | |
147 | if (ret) | |
148 | return ret; | |
149 | ||
150 | if (!val) | |
151 | return count; | |
152 | ||
153 | pm_runtime_get_sync(&usb4->dev); | |
154 | ||
155 | if (mutex_lock_interruptible(&tb->lock)) { | |
156 | ret = -ERESTARTSYS; | |
157 | goto out_rpm; | |
158 | } | |
159 | ||
160 | /* Must be in offline mode already */ | |
161 | if (!usb4->offline) { | |
162 | ret = -EINVAL; | |
163 | goto out_unlock; | |
164 | } | |
165 | ||
166 | tb_retimer_remove_all(port); | |
167 | ret = tb_retimer_scan(port, true); | |
168 | ||
169 | out_unlock: | |
170 | mutex_unlock(&tb->lock); | |
171 | out_rpm: | |
172 | pm_runtime_mark_last_busy(&usb4->dev); | |
173 | pm_runtime_put_autosuspend(&usb4->dev); | |
174 | ||
175 | return ret ? ret : count; | |
176 | } | |
177 | static DEVICE_ATTR_WO(rescan); | |
178 | ||
179 | static struct attribute *service_attrs[] = { | |
180 | &dev_attr_offline.attr, | |
181 | &dev_attr_rescan.attr, | |
182 | NULL | |
183 | }; | |
184 | ||
185 | static umode_t service_attr_is_visible(struct kobject *kobj, | |
186 | struct attribute *attr, int n) | |
187 | { | |
188 | struct device *dev = kobj_to_dev(kobj); | |
189 | struct usb4_port *usb4 = tb_to_usb4_port_device(dev); | |
190 | ||
191 | /* | |
192 | * Always need some platform help to cycle the modes so that | |
193 | * retimers can be accessed through the sideband. | |
194 | */ | |
195 | return usb4->can_offline ? attr->mode : 0; | |
196 | } | |
197 | ||
198 | static const struct attribute_group service_group = { | |
199 | .attrs = service_attrs, | |
200 | .is_visible = service_attr_is_visible, | |
201 | }; | |
202 | ||
cae5f515 MW |
203 | static const struct attribute_group *usb4_port_device_groups[] = { |
204 | &common_group, | |
3fb10ea4 | 205 | &service_group, |
cae5f515 MW |
206 | NULL |
207 | }; | |
208 | ||
209 | static void usb4_port_device_release(struct device *dev) | |
210 | { | |
211 | struct usb4_port *usb4 = container_of(dev, struct usb4_port, dev); | |
212 | ||
213 | kfree(usb4); | |
214 | } | |
215 | ||
216 | struct device_type usb4_port_device_type = { | |
217 | .name = "usb4_port", | |
218 | .groups = usb4_port_device_groups, | |
219 | .release = usb4_port_device_release, | |
220 | }; | |
221 | ||
222 | /** | |
223 | * usb4_port_device_add() - Add USB4 port device | |
224 | * @port: Lane 0 adapter port to add the USB4 port | |
225 | * | |
226 | * Creates and registers a USB4 port device for @port. Returns the new | |
227 | * USB4 port device pointer or ERR_PTR() in case of error. | |
228 | */ | |
229 | struct usb4_port *usb4_port_device_add(struct tb_port *port) | |
230 | { | |
231 | struct usb4_port *usb4; | |
232 | int ret; | |
233 | ||
234 | usb4 = kzalloc(sizeof(*usb4), GFP_KERNEL); | |
235 | if (!usb4) | |
236 | return ERR_PTR(-ENOMEM); | |
237 | ||
238 | usb4->port = port; | |
239 | usb4->dev.type = &usb4_port_device_type; | |
240 | usb4->dev.parent = &port->sw->dev; | |
241 | dev_set_name(&usb4->dev, "usb4_port%d", port->port); | |
242 | ||
243 | ret = device_register(&usb4->dev); | |
244 | if (ret) { | |
245 | put_device(&usb4->dev); | |
246 | return ERR_PTR(ret); | |
247 | } | |
248 | ||
249 | pm_runtime_no_callbacks(&usb4->dev); | |
250 | pm_runtime_set_active(&usb4->dev); | |
251 | pm_runtime_enable(&usb4->dev); | |
252 | pm_runtime_set_autosuspend_delay(&usb4->dev, TB_AUTOSUSPEND_DELAY); | |
253 | pm_runtime_mark_last_busy(&usb4->dev); | |
254 | pm_runtime_use_autosuspend(&usb4->dev); | |
255 | ||
256 | return usb4; | |
257 | } | |
258 | ||
259 | /** | |
260 | * usb4_port_device_remove() - Removes USB4 port device | |
261 | * @usb4: USB4 port device | |
262 | * | |
263 | * Unregisters the USB4 port device from the system. The device will be | |
264 | * released when the last reference is dropped. | |
265 | */ | |
266 | void usb4_port_device_remove(struct usb4_port *usb4) | |
267 | { | |
268 | device_unregister(&usb4->dev); | |
269 | } | |
3fb10ea4 RM |
270 | |
271 | /** | |
272 | * usb4_port_device_resume() - Resumes USB4 port device | |
273 | * @usb4: USB4 port device | |
274 | * | |
275 | * Used to resume USB4 port device after sleep state. | |
276 | */ | |
277 | int usb4_port_device_resume(struct usb4_port *usb4) | |
278 | { | |
279 | return usb4->offline ? usb4_port_offline(usb4) : 0; | |
280 | } |