1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Driver for Intel PMC USB mux control |
4 | * |
5 | * Copyright (C) 2020 Intel Corporation |
6 | * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> |
7 | */ |
8 | |
9 | #include <linux/acpi.h> |
10 | #include <linux/module.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/property.h> |
13 | #include <linux/usb/pd.h> |
14 | #include <linux/usb/role.h> |
15 | #include <linux/usb/typec_mux.h> |
16 | #include <linux/usb/typec_dp.h> |
17 | #include <linux/usb/typec_tbt.h> |
18 | #include <linux/debugfs.h> |
19 | #include <linux/usb.h> |
20 | |
21 | #include <asm/intel_scu_ipc.h> |
22 | |
23 | #define PMC_USBC_CMD 0xa7 |
24 | |
25 | /* Response status bits */ |
26 | #define PMC_USB_RESP_STATUS_FAILURE BIT(0) |
27 | #define PMC_USB_RESP_STATUS_FATAL BIT(1) |
28 | |
29 | /* "Usage" OOB Message field values */ |
30 | enum { |
31 | PMC_USB_CONNECT, |
32 | PMC_USB_DISCONNECT, |
33 | PMC_USB_SAFE_MODE, |
34 | PMC_USB_ALT_MODE, |
35 | PMC_USB_DP_HPD, |
36 | }; |
37 | |
38 | #define PMC_USB_MSG_USB2_PORT_SHIFT 0 |
39 | #define PMC_USB_MSG_USB3_PORT_SHIFT 4 |
40 | #define PMC_USB_MSG_UFP_SHIFT 4 |
41 | #define PMC_USB_MSG_ORI_HSL_SHIFT 5 |
42 | #define PMC_USB_MSG_ORI_AUX_SHIFT 6 |
43 | |
44 | /* Alt Mode Request */ |
45 | struct altmode_req { |
46 | u8 usage; |
47 | u8 mode_type; |
48 | u8 mode_id; |
49 | u8 reserved; |
50 | u32 mode_data; |
51 | } __packed; |
52 | |
53 | #define PMC_USB_MODE_TYPE_SHIFT 4 |
54 | |
55 | enum { |
56 | PMC_USB_MODE_TYPE_USB, |
57 | PMC_USB_MODE_TYPE_DP, |
58 | PMC_USB_MODE_TYPE_TBT, |
59 | }; |
60 | |
61 | /* Common Mode Data bits */ |
62 | #define PMC_USB_ALTMODE_RETIMER_CABLE BIT(2) |
63 | |
64 | #define PMC_USB_ALTMODE_ORI_SHIFT 1 |
65 | #define PMC_USB_ALTMODE_UFP_SHIFT 3 |
66 | |
67 | /* DP specific Mode Data bits */ |
68 | #define PMC_USB_ALTMODE_DP_MODE_SHIFT 8 |
69 | |
70 | /* TBT specific Mode Data bits */ |
71 | #define PMC_USB_ALTMODE_TBT_TYPE BIT(17) |
72 | #define PMC_USB_ALTMODE_CABLE_TYPE BIT(18) |
73 | #define PMC_USB_ALTMODE_ACTIVE_LINK BIT(20) |
74 | #define PMC_USB_ALTMODE_ACTIVE_CABLE BIT(22) |
75 | #define PMC_USB_ALTMODE_FORCE_LSR BIT(23) |
76 | #define PMC_USB_ALTMODE_CABLE_SPD(_s_) (((_s_) & GENMASK(2, 0)) << 25) |
77 | #define PMC_USB_ALTMODE_CABLE_USB31 1 |
78 | #define PMC_USB_ALTMODE_CABLE_10GPS 2 |
79 | #define PMC_USB_ALTMODE_CABLE_20GPS 3 |
80 | #define PMC_USB_ALTMODE_TBT_GEN(_g_) (((_g_) & GENMASK(1, 0)) << 28) |
81 | |
82 | /* Display HPD Request bits */ |
83 | #define PMC_USB_DP_HPD_LVL BIT(4) |
84 | #define PMC_USB_DP_HPD_IRQ BIT(5) |
85 | |
86 | /* |
87 | * Input Output Manager (IOM) PORT STATUS |
88 | */ |
89 | #define IOM_PORT_STATUS_ACTIVITY_TYPE_MASK GENMASK(9, 6) |
90 | #define IOM_PORT_STATUS_ACTIVITY_TYPE_SHIFT 6 |
91 | #define IOM_PORT_STATUS_ACTIVITY_TYPE_USB 0x03 |
92 | /* activity type: Safe Mode */ |
93 | #define IOM_PORT_STATUS_ACTIVITY_TYPE_SAFE_MODE 0x04 |
94 | /* activity type: Display Port */ |
95 | #define IOM_PORT_STATUS_ACTIVITY_TYPE_DP 0x05 |
96 | /* activity type: Display Port Multi Function Device */ |
97 | #define IOM_PORT_STATUS_ACTIVITY_TYPE_DP_MFD 0x06 |
98 | /* activity type: Thunderbolt */ |
99 | #define IOM_PORT_STATUS_ACTIVITY_TYPE_TBT 0x07 |
100 | #define IOM_PORT_STATUS_ACTIVITY_TYPE_ALT_MODE_USB 0x0c |
101 | #define IOM_PORT_STATUS_ACTIVITY_TYPE_ALT_MODE_TBT_USB 0x0d |
102 | /* Upstream Facing Port Information */ |
103 | #define IOM_PORT_STATUS_UFP BIT(10) |
104 | /* Display Port Hot Plug Detect status */ |
105 | #define IOM_PORT_STATUS_DHPD_HPD_STATUS_MASK GENMASK(13, 12) |
106 | #define IOM_PORT_STATUS_DHPD_HPD_STATUS_SHIFT 12 |
107 | #define IOM_PORT_STATUS_DHPD_HPD_STATUS_ASSERT 0x01 |
108 | #define IOM_PORT_STATUS_DHPD_HPD_SOURCE_TBT BIT(14) |
109 | #define IOM_PORT_STATUS_CONNECTED BIT(31) |
110 | |
111 | #define IOM_PORT_ACTIVITY_IS(_status_, _type_) \ |
112 | ((((_status_) & IOM_PORT_STATUS_ACTIVITY_TYPE_MASK) >> \ |
113 | IOM_PORT_STATUS_ACTIVITY_TYPE_SHIFT) == \ |
114 | (IOM_PORT_STATUS_ACTIVITY_TYPE_##_type_)) |
115 | |
116 | #define IOM_PORT_HPD_ASSERTED(_status_) \ |
117 | ((((_status_) & IOM_PORT_STATUS_DHPD_HPD_STATUS_MASK) >> \ |
118 | IOM_PORT_STATUS_DHPD_HPD_STATUS_SHIFT) & \ |
119 | IOM_PORT_STATUS_DHPD_HPD_STATUS_ASSERT) |
120 | |
121 | /* IOM port status register */ |
122 | #define IOM_PORT_STATUS_REGS(_offset_, _size_) ((_offset_) | (_size_)) |
123 | #define IOM_PORT_STATUS_REGS_SZ_MASK BIT(0) |
124 | #define IOM_PORT_STATUS_REGS_SZ_4 0 |
125 | #define IOM_PORT_STATUS_REGS_SZ_8 1 |
126 | #define IOM_PORT_STATUS_REGS_OFFSET(_d_) \ |
127 | ((_d_) & ~IOM_PORT_STATUS_REGS_SZ_MASK) |
128 | #define IOM_PORT_STATUS_REGS_SIZE(_d_) \ |
129 | (4 << ((_d_) & IOM_PORT_STATUS_REGS_SZ_MASK)) |
130 | |
131 | struct pmc_usb; |
132 | |
133 | struct pmc_usb_port { |
134 | int num; |
135 | u32 iom_status; |
136 | struct pmc_usb *pmc; |
137 | struct typec_mux_dev *typec_mux; |
138 | struct typec_switch_dev *typec_sw; |
139 | struct usb_role_switch *usb_sw; |
140 | |
141 | enum typec_orientation orientation; |
142 | enum usb_role role; |
143 | |
144 | u8 usb2_port; |
145 | u8 usb3_port; |
146 | |
147 | enum typec_orientation sbu_orientation; |
148 | enum typec_orientation hsl_orientation; |
149 | }; |
150 | |
151 | struct pmc_usb { |
152 | u8 num_ports; |
153 | struct device *dev; |
154 | struct intel_scu_ipc_dev *ipc; |
155 | struct pmc_usb_port *port; |
156 | struct acpi_device *iom_adev; |
157 | void __iomem *iom_base; |
158 | u32 iom_port_status_offset; |
159 | u8 iom_port_status_size; |
160 | |
161 | struct dentry *dentry; |
162 | }; |
163 | |
164 | static struct dentry *pmc_mux_debugfs_root; |
165 | |
166 | static void update_port_status(struct pmc_usb_port *port) |
167 | { |
168 | u8 port_num; |
169 | |
170 | /* SoC expects the USB Type-C port numbers to start with 0 */ |
171 | port_num = port->usb3_port - 1; |
172 | |
173 | port->iom_status = readl(addr: port->pmc->iom_base + |
174 | port->pmc->iom_port_status_offset + |
175 | port_num * port->pmc->iom_port_status_size); |
176 | } |
177 | |
178 | static int sbu_orientation(struct pmc_usb_port *port) |
179 | { |
180 | if (port->sbu_orientation) |
181 | return port->sbu_orientation - 1; |
182 | |
183 | return port->orientation - 1; |
184 | } |
185 | |
186 | static int hsl_orientation(struct pmc_usb_port *port) |
187 | { |
188 | if (port->hsl_orientation) |
189 | return port->hsl_orientation - 1; |
190 | |
191 | return port->orientation - 1; |
192 | } |
193 | |
194 | static bool is_pmc_mux_tbt(struct acpi_device *adev) |
195 | { |
196 | return acpi_dev_hid_uid_match(adev, "INTC1072" , NULL) || |
197 | acpi_dev_hid_uid_match(adev, "INTC1079" , NULL); |
198 | } |
199 | |
200 | static int pmc_usb_send_command(struct intel_scu_ipc_dev *ipc, u8 *msg, u32 len) |
201 | { |
202 | u8 response[4]; |
203 | u8 status_res; |
204 | int ret; |
205 | |
206 | /* |
207 | * Error bit will always be 0 with the USBC command. |
208 | * Status can be checked from the response message if the |
209 | * function intel_scu_ipc_dev_command succeeds. |
210 | */ |
211 | ret = intel_scu_ipc_dev_command(scu: ipc, PMC_USBC_CMD, sub: 0, in: msg, |
212 | inlen: len, out: response, outlen: sizeof(response)); |
213 | |
214 | if (ret) |
215 | return ret; |
216 | |
217 | status_res = (msg[0] & 0xf) < PMC_USB_SAFE_MODE ? |
218 | response[2] : response[1]; |
219 | |
220 | if (status_res & PMC_USB_RESP_STATUS_FAILURE) { |
221 | if (status_res & PMC_USB_RESP_STATUS_FATAL) |
222 | return -EIO; |
223 | |
224 | return -EBUSY; |
225 | } |
226 | |
227 | return 0; |
228 | } |
229 | |
230 | static int pmc_usb_command(struct pmc_usb_port *port, u8 *msg, u32 len) |
231 | { |
232 | int retry_count = 3; |
233 | int ret; |
234 | |
235 | /* |
236 | * If PMC is busy then retry the command once again |
237 | */ |
238 | while (retry_count--) { |
239 | ret = pmc_usb_send_command(ipc: port->pmc->ipc, msg, len); |
240 | if (ret != -EBUSY) |
241 | break; |
242 | } |
243 | |
244 | return ret; |
245 | } |
246 | |
247 | static int |
248 | pmc_usb_mux_dp_hpd(struct pmc_usb_port *port, struct typec_displayport_data *dp) |
249 | { |
250 | u8 msg[2] = { }; |
251 | int ret; |
252 | |
253 | msg[0] = PMC_USB_DP_HPD; |
254 | msg[0] |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT; |
255 | |
256 | /* Configure HPD first if HPD,IRQ comes together */ |
257 | if (!IOM_PORT_HPD_ASSERTED(port->iom_status) && |
258 | dp->status & DP_STATUS_IRQ_HPD && |
259 | dp->status & DP_STATUS_HPD_STATE) { |
260 | msg[1] = PMC_USB_DP_HPD_LVL; |
261 | ret = pmc_usb_command(port, msg, len: sizeof(msg)); |
262 | if (ret) |
263 | return ret; |
264 | } |
265 | |
266 | if (dp->status & DP_STATUS_IRQ_HPD) |
267 | msg[1] = PMC_USB_DP_HPD_IRQ; |
268 | |
269 | if (dp->status & DP_STATUS_HPD_STATE) |
270 | msg[1] |= PMC_USB_DP_HPD_LVL; |
271 | |
272 | return pmc_usb_command(port, msg, len: sizeof(msg)); |
273 | } |
274 | |
275 | static int |
276 | pmc_usb_mux_dp(struct pmc_usb_port *port, struct typec_mux_state *state) |
277 | { |
278 | struct typec_displayport_data *data = state->data; |
279 | struct altmode_req req = { }; |
280 | int ret; |
281 | |
282 | if (IOM_PORT_ACTIVITY_IS(port->iom_status, DP) || |
283 | IOM_PORT_ACTIVITY_IS(port->iom_status, DP_MFD)) { |
284 | if (IOM_PORT_HPD_ASSERTED(port->iom_status) && |
285 | (!(data->status & DP_STATUS_IRQ_HPD) && |
286 | data->status & DP_STATUS_HPD_STATE)) |
287 | return 0; |
288 | |
289 | return pmc_usb_mux_dp_hpd(port, dp: state->data); |
290 | } |
291 | |
292 | req.usage = PMC_USB_ALT_MODE; |
293 | req.usage |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT; |
294 | req.mode_type = PMC_USB_MODE_TYPE_DP << PMC_USB_MODE_TYPE_SHIFT; |
295 | |
296 | req.mode_data = (port->orientation - 1) << PMC_USB_ALTMODE_ORI_SHIFT; |
297 | req.mode_data |= (port->role - 1) << PMC_USB_ALTMODE_UFP_SHIFT; |
298 | |
299 | req.mode_data |= (state->mode - TYPEC_STATE_MODAL) << |
300 | PMC_USB_ALTMODE_DP_MODE_SHIFT; |
301 | |
302 | if (!is_pmc_mux_tbt(adev: port->pmc->iom_adev)) { |
303 | u8 cable_speed = (data->conf & DP_CONF_SIGNALLING_MASK) >> |
304 | DP_CONF_SIGNALLING_SHIFT; |
305 | |
306 | u8 cable_type = (data->conf & DP_CONF_CABLE_TYPE_MASK) >> |
307 | DP_CONF_CABLE_TYPE_SHIFT; |
308 | |
309 | req.mode_data |= PMC_USB_ALTMODE_CABLE_SPD(cable_speed); |
310 | |
311 | if (cable_type == DP_CONF_CABLE_TYPE_OPTICAL) |
312 | req.mode_data |= PMC_USB_ALTMODE_CABLE_TYPE; |
313 | else if (cable_type == DP_CONF_CABLE_TYPE_RE_TIMER) |
314 | req.mode_data |= PMC_USB_ALTMODE_ACTIVE_CABLE | |
315 | PMC_USB_ALTMODE_RETIMER_CABLE; |
316 | else if (cable_type == DP_CONF_CABLE_TYPE_RE_DRIVER) |
317 | req.mode_data |= PMC_USB_ALTMODE_ACTIVE_CABLE; |
318 | } |
319 | |
320 | ret = pmc_usb_command(port, msg: (void *)&req, len: sizeof(req)); |
321 | if (ret) |
322 | return ret; |
323 | |
324 | if (data->status & (DP_STATUS_IRQ_HPD | DP_STATUS_HPD_STATE)) |
325 | return pmc_usb_mux_dp_hpd(port, dp: state->data); |
326 | |
327 | return 0; |
328 | } |
329 | |
330 | static int |
331 | pmc_usb_mux_tbt(struct pmc_usb_port *port, struct typec_mux_state *state) |
332 | { |
333 | struct typec_thunderbolt_data *data = state->data; |
334 | u8 cable_rounded = TBT_CABLE_ROUNDED_SUPPORT(data->cable_mode); |
335 | u8 cable_speed = TBT_CABLE_SPEED(data->cable_mode); |
336 | struct altmode_req req = { }; |
337 | |
338 | if (IOM_PORT_ACTIVITY_IS(port->iom_status, TBT) || |
339 | IOM_PORT_ACTIVITY_IS(port->iom_status, ALT_MODE_TBT_USB)) |
340 | return 0; |
341 | |
342 | req.usage = PMC_USB_ALT_MODE; |
343 | req.usage |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT; |
344 | req.mode_type = PMC_USB_MODE_TYPE_TBT << PMC_USB_MODE_TYPE_SHIFT; |
345 | |
346 | req.mode_data = (port->orientation - 1) << PMC_USB_ALTMODE_ORI_SHIFT; |
347 | req.mode_data |= (port->role - 1) << PMC_USB_ALTMODE_UFP_SHIFT; |
348 | |
349 | if (TBT_ADAPTER(data->device_mode) == TBT_ADAPTER_TBT3) |
350 | req.mode_data |= PMC_USB_ALTMODE_TBT_TYPE; |
351 | |
352 | if (data->cable_mode & TBT_CABLE_OPTICAL) |
353 | req.mode_data |= PMC_USB_ALTMODE_CABLE_TYPE; |
354 | |
355 | if (data->cable_mode & TBT_CABLE_LINK_TRAINING) |
356 | req.mode_data |= PMC_USB_ALTMODE_ACTIVE_LINK; |
357 | |
358 | if (acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1072" , NULL) || |
359 | acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1079" , NULL)) { |
360 | if ((data->enter_vdo & TBT_ENTER_MODE_ACTIVE_CABLE) || |
361 | (data->cable_mode & TBT_CABLE_RETIMER)) |
362 | req.mode_data |= PMC_USB_ALTMODE_RETIMER_CABLE; |
363 | } else { |
364 | if (data->enter_vdo & TBT_ENTER_MODE_ACTIVE_CABLE) |
365 | req.mode_data |= PMC_USB_ALTMODE_ACTIVE_CABLE; |
366 | |
367 | if (data->cable_mode & TBT_CABLE_RETIMER) |
368 | req.mode_data |= PMC_USB_ALTMODE_RETIMER_CABLE; |
369 | } |
370 | |
371 | req.mode_data |= PMC_USB_ALTMODE_CABLE_SPD(cable_speed); |
372 | |
373 | req.mode_data |= PMC_USB_ALTMODE_TBT_GEN(cable_rounded); |
374 | |
375 | return pmc_usb_command(port, msg: (void *)&req, len: sizeof(req)); |
376 | } |
377 | |
378 | static int |
379 | pmc_usb_mux_usb4(struct pmc_usb_port *port, struct typec_mux_state *state) |
380 | { |
381 | struct enter_usb_data *data = state->data; |
382 | struct altmode_req req = { }; |
383 | u8 cable_speed; |
384 | |
385 | if (IOM_PORT_ACTIVITY_IS(port->iom_status, TBT) || |
386 | IOM_PORT_ACTIVITY_IS(port->iom_status, ALT_MODE_TBT_USB)) |
387 | return 0; |
388 | |
389 | req.usage = PMC_USB_ALT_MODE; |
390 | req.usage |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT; |
391 | req.mode_type = PMC_USB_MODE_TYPE_TBT << PMC_USB_MODE_TYPE_SHIFT; |
392 | |
393 | /* USB4 Mode */ |
394 | req.mode_data = PMC_USB_ALTMODE_FORCE_LSR; |
395 | |
396 | if (data->active_link_training) |
397 | req.mode_data |= PMC_USB_ALTMODE_ACTIVE_LINK; |
398 | |
399 | req.mode_data |= (port->orientation - 1) << PMC_USB_ALTMODE_ORI_SHIFT; |
400 | req.mode_data |= (port->role - 1) << PMC_USB_ALTMODE_UFP_SHIFT; |
401 | |
402 | switch ((data->eudo & EUDO_CABLE_TYPE_MASK) >> EUDO_CABLE_TYPE_SHIFT) { |
403 | case EUDO_CABLE_TYPE_PASSIVE: |
404 | break; |
405 | case EUDO_CABLE_TYPE_OPTICAL: |
406 | req.mode_data |= PMC_USB_ALTMODE_CABLE_TYPE; |
407 | fallthrough; |
408 | case EUDO_CABLE_TYPE_RE_TIMER: |
409 | if (!acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1072" , NULL) || |
410 | !acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1079" , NULL)) |
411 | req.mode_data |= PMC_USB_ALTMODE_RETIMER_CABLE; |
412 | fallthrough; |
413 | default: |
414 | if (acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1072" , NULL) || |
415 | acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1079" , NULL)) |
416 | req.mode_data |= PMC_USB_ALTMODE_RETIMER_CABLE; |
417 | else |
418 | req.mode_data |= PMC_USB_ALTMODE_ACTIVE_CABLE; |
419 | |
420 | /* Configure data rate to rounded in the case of Active TBT3 |
421 | * and USB4 cables. |
422 | */ |
423 | req.mode_data |= PMC_USB_ALTMODE_TBT_GEN(1); |
424 | break; |
425 | } |
426 | |
427 | cable_speed = (data->eudo & EUDO_CABLE_SPEED_MASK) >> EUDO_CABLE_SPEED_SHIFT; |
428 | req.mode_data |= PMC_USB_ALTMODE_CABLE_SPD(cable_speed); |
429 | |
430 | return pmc_usb_command(port, msg: (void *)&req, len: sizeof(req)); |
431 | } |
432 | |
433 | static int pmc_usb_mux_safe_state(struct pmc_usb_port *port, |
434 | struct typec_mux_state *state) |
435 | { |
436 | u8 msg; |
437 | |
438 | if (IOM_PORT_ACTIVITY_IS(port->iom_status, SAFE_MODE)) |
439 | return 0; |
440 | |
441 | if ((IOM_PORT_ACTIVITY_IS(port->iom_status, DP) || |
442 | IOM_PORT_ACTIVITY_IS(port->iom_status, DP_MFD)) && |
443 | state->alt && state->alt->svid == USB_TYPEC_DP_SID) |
444 | return 0; |
445 | |
446 | if ((IOM_PORT_ACTIVITY_IS(port->iom_status, TBT) || |
447 | IOM_PORT_ACTIVITY_IS(port->iom_status, ALT_MODE_TBT_USB)) && |
448 | state->alt && state->alt->svid == USB_TYPEC_TBT_SID) |
449 | return 0; |
450 | |
451 | msg = PMC_USB_SAFE_MODE; |
452 | msg |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT; |
453 | |
454 | return pmc_usb_command(port, msg: &msg, len: sizeof(msg)); |
455 | } |
456 | |
457 | static int pmc_usb_disconnect(struct pmc_usb_port *port) |
458 | { |
459 | struct typec_displayport_data data = { }; |
460 | u8 msg[2]; |
461 | |
462 | if (!(port->iom_status & IOM_PORT_STATUS_CONNECTED)) |
463 | return 0; |
464 | |
465 | /* Clear DisplayPort HPD if it's still asserted. */ |
466 | if (IOM_PORT_HPD_ASSERTED(port->iom_status)) |
467 | pmc_usb_mux_dp_hpd(port, dp: &data); |
468 | |
469 | msg[0] = PMC_USB_DISCONNECT; |
470 | msg[0] |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT; |
471 | |
472 | msg[1] = port->usb2_port << PMC_USB_MSG_USB2_PORT_SHIFT; |
473 | |
474 | return pmc_usb_command(port, msg, len: sizeof(msg)); |
475 | } |
476 | |
477 | static int pmc_usb_connect(struct pmc_usb_port *port, enum usb_role role) |
478 | { |
479 | u8 ufp = role == USB_ROLE_DEVICE ? 1 : 0; |
480 | u8 msg[2]; |
481 | int ret; |
482 | |
483 | if (port->orientation == TYPEC_ORIENTATION_NONE) |
484 | return -EINVAL; |
485 | |
486 | if (port->iom_status & IOM_PORT_STATUS_CONNECTED) { |
487 | if (port->role == role || port->role == USB_ROLE_NONE) |
488 | return 0; |
489 | |
490 | /* Role swap */ |
491 | ret = pmc_usb_disconnect(port); |
492 | if (ret) |
493 | return ret; |
494 | } |
495 | |
496 | msg[0] = PMC_USB_CONNECT; |
497 | msg[0] |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT; |
498 | |
499 | msg[1] = port->usb2_port << PMC_USB_MSG_USB2_PORT_SHIFT; |
500 | msg[1] |= ufp << PMC_USB_MSG_UFP_SHIFT; |
501 | msg[1] |= hsl_orientation(port) << PMC_USB_MSG_ORI_HSL_SHIFT; |
502 | msg[1] |= sbu_orientation(port) << PMC_USB_MSG_ORI_AUX_SHIFT; |
503 | |
504 | return pmc_usb_command(port, msg, len: sizeof(msg)); |
505 | } |
506 | |
507 | static int |
508 | pmc_usb_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state) |
509 | { |
510 | struct pmc_usb_port *port = typec_mux_get_drvdata(mux); |
511 | |
512 | update_port_status(port); |
513 | |
514 | if (port->orientation == TYPEC_ORIENTATION_NONE || port->role == USB_ROLE_NONE) |
515 | return 0; |
516 | |
517 | if (state->mode == TYPEC_STATE_SAFE) |
518 | return pmc_usb_mux_safe_state(port, state); |
519 | if (state->mode == TYPEC_STATE_USB) |
520 | return pmc_usb_connect(port, role: port->role); |
521 | |
522 | if (state->alt) { |
523 | switch (state->alt->svid) { |
524 | case USB_TYPEC_TBT_SID: |
525 | return pmc_usb_mux_tbt(port, state); |
526 | case USB_TYPEC_DP_SID: |
527 | return pmc_usb_mux_dp(port, state); |
528 | } |
529 | } else { |
530 | switch (state->mode) { |
531 | case TYPEC_MODE_USB2: |
532 | /* REVISIT: Try with usb3_port set to 0? */ |
533 | break; |
534 | case TYPEC_MODE_USB3: |
535 | return pmc_usb_connect(port, role: port->role); |
536 | case TYPEC_MODE_USB4: |
537 | return pmc_usb_mux_usb4(port, state); |
538 | } |
539 | } |
540 | |
541 | return -EOPNOTSUPP; |
542 | } |
543 | |
544 | static int pmc_usb_set_orientation(struct typec_switch_dev *sw, |
545 | enum typec_orientation orientation) |
546 | { |
547 | struct pmc_usb_port *port = typec_switch_get_drvdata(sw); |
548 | |
549 | update_port_status(port); |
550 | |
551 | port->orientation = orientation; |
552 | |
553 | return 0; |
554 | } |
555 | |
556 | static int pmc_usb_set_role(struct usb_role_switch *sw, enum usb_role role) |
557 | { |
558 | struct pmc_usb_port *port = usb_role_switch_get_drvdata(sw); |
559 | int ret; |
560 | |
561 | update_port_status(port); |
562 | |
563 | if (role == USB_ROLE_NONE) |
564 | ret = pmc_usb_disconnect(port); |
565 | else |
566 | ret = pmc_usb_connect(port, role); |
567 | |
568 | port->role = role; |
569 | |
570 | return ret; |
571 | } |
572 | |
573 | static int pmc_usb_register_port(struct pmc_usb *pmc, int index, |
574 | struct fwnode_handle *fwnode) |
575 | { |
576 | struct pmc_usb_port *port = &pmc->port[index]; |
577 | struct usb_role_switch_desc desc = { }; |
578 | struct typec_switch_desc sw_desc = { }; |
579 | struct typec_mux_desc mux_desc = { }; |
580 | const char *str; |
581 | int ret; |
582 | |
583 | ret = fwnode_property_read_u8(fwnode, propname: "usb2-port-number" , val: &port->usb2_port); |
584 | if (ret) |
585 | return ret; |
586 | |
587 | ret = fwnode_property_read_u8(fwnode, propname: "usb3-port-number" , val: &port->usb3_port); |
588 | if (ret) |
589 | return ret; |
590 | |
591 | ret = fwnode_property_read_string(fwnode, propname: "sbu-orientation" , val: &str); |
592 | if (!ret) |
593 | port->sbu_orientation = typec_find_orientation(name: str); |
594 | |
595 | ret = fwnode_property_read_string(fwnode, propname: "hsl-orientation" , val: &str); |
596 | if (!ret) |
597 | port->hsl_orientation = typec_find_orientation(name: str); |
598 | |
599 | port->num = index; |
600 | port->pmc = pmc; |
601 | |
602 | sw_desc.fwnode = fwnode; |
603 | sw_desc.drvdata = port; |
604 | sw_desc.name = fwnode_get_name(fwnode); |
605 | sw_desc.set = pmc_usb_set_orientation; |
606 | |
607 | port->typec_sw = typec_switch_register(parent: pmc->dev, desc: &sw_desc); |
608 | if (IS_ERR(ptr: port->typec_sw)) |
609 | return PTR_ERR(ptr: port->typec_sw); |
610 | |
611 | mux_desc.fwnode = fwnode; |
612 | mux_desc.drvdata = port; |
613 | mux_desc.name = fwnode_get_name(fwnode); |
614 | mux_desc.set = pmc_usb_mux_set; |
615 | |
616 | port->typec_mux = typec_mux_register(parent: pmc->dev, desc: &mux_desc); |
617 | if (IS_ERR(ptr: port->typec_mux)) { |
618 | ret = PTR_ERR(ptr: port->typec_mux); |
619 | goto err_unregister_switch; |
620 | } |
621 | |
622 | desc.fwnode = fwnode; |
623 | desc.driver_data = port; |
624 | desc.name = fwnode_get_name(fwnode); |
625 | desc.set = pmc_usb_set_role; |
626 | desc.allow_userspace_control = true; |
627 | |
628 | port->usb_sw = usb_role_switch_register(parent: pmc->dev, desc: &desc); |
629 | if (IS_ERR(ptr: port->usb_sw)) { |
630 | ret = PTR_ERR(ptr: port->usb_sw); |
631 | goto err_unregister_mux; |
632 | } |
633 | |
634 | return 0; |
635 | |
636 | err_unregister_mux: |
637 | typec_mux_unregister(mux: port->typec_mux); |
638 | |
639 | err_unregister_switch: |
640 | typec_switch_unregister(sw: port->typec_sw); |
641 | |
642 | return ret; |
643 | } |
644 | |
645 | /* IOM ACPI IDs and IOM_PORT_STATUS_OFFSET */ |
646 | static const struct acpi_device_id iom_acpi_ids[] = { |
647 | /* TigerLake */ |
648 | { "INTC1072" , IOM_PORT_STATUS_REGS(0x560, IOM_PORT_STATUS_REGS_SZ_4) }, |
649 | |
650 | /* AlderLake */ |
651 | { "INTC1079" , IOM_PORT_STATUS_REGS(0x160, IOM_PORT_STATUS_REGS_SZ_4) }, |
652 | |
653 | /* Meteor Lake */ |
654 | { "INTC107A" , IOM_PORT_STATUS_REGS(0x160, IOM_PORT_STATUS_REGS_SZ_4) }, |
655 | |
656 | /* Lunar Lake */ |
657 | { "INTC10EA" , IOM_PORT_STATUS_REGS(0x150, IOM_PORT_STATUS_REGS_SZ_8) }, |
658 | {} |
659 | }; |
660 | |
661 | static int pmc_usb_probe_iom(struct pmc_usb *pmc) |
662 | { |
663 | struct list_head resource_list; |
664 | struct resource_entry *rentry; |
665 | static const struct acpi_device_id *dev_id; |
666 | struct acpi_device *adev = NULL; |
667 | int ret; |
668 | |
669 | for (dev_id = &iom_acpi_ids[0]; dev_id->id[0]; dev_id++) { |
670 | adev = acpi_dev_get_first_match_dev(hid: dev_id->id, NULL, hrv: -1); |
671 | if (adev) |
672 | break; |
673 | } |
674 | if (!adev) |
675 | return -ENODEV; |
676 | |
677 | pmc->iom_port_status_offset = IOM_PORT_STATUS_REGS_OFFSET(dev_id->driver_data); |
678 | pmc->iom_port_status_size = IOM_PORT_STATUS_REGS_SIZE(dev_id->driver_data); |
679 | |
680 | INIT_LIST_HEAD(list: &resource_list); |
681 | ret = acpi_dev_get_memory_resources(adev, list: &resource_list); |
682 | if (ret < 0) { |
683 | acpi_dev_put(adev); |
684 | return ret; |
685 | } |
686 | |
687 | rentry = list_first_entry_or_null(&resource_list, struct resource_entry, node); |
688 | if (rentry) |
689 | pmc->iom_base = devm_ioremap_resource(dev: pmc->dev, res: rentry->res); |
690 | |
691 | acpi_dev_free_resource_list(list: &resource_list); |
692 | |
693 | if (!pmc->iom_base) { |
694 | acpi_dev_put(adev); |
695 | return -ENOMEM; |
696 | } |
697 | |
698 | if (IS_ERR(ptr: pmc->iom_base)) { |
699 | acpi_dev_put(adev); |
700 | return PTR_ERR(ptr: pmc->iom_base); |
701 | } |
702 | |
703 | pmc->iom_adev = adev; |
704 | |
705 | return 0; |
706 | } |
707 | |
708 | static int port_iom_status_show(struct seq_file *s, void *unused) |
709 | { |
710 | struct pmc_usb_port *port = s->private; |
711 | |
712 | update_port_status(port); |
713 | seq_printf(m: s, fmt: "0x%08x\n" , port->iom_status); |
714 | |
715 | return 0; |
716 | } |
717 | DEFINE_SHOW_ATTRIBUTE(port_iom_status); |
718 | |
719 | static void pmc_mux_port_debugfs_init(struct pmc_usb_port *port) |
720 | { |
721 | struct dentry *debugfs_dir; |
722 | char name[6]; |
723 | |
724 | snprintf(buf: name, size: sizeof(name), fmt: "port%d" , port->usb3_port - 1); |
725 | |
726 | debugfs_dir = debugfs_create_dir(name, parent: port->pmc->dentry); |
727 | debugfs_create_file(name: "iom_status" , mode: 0400, parent: debugfs_dir, data: port, |
728 | fops: &port_iom_status_fops); |
729 | } |
730 | |
731 | static int pmc_usb_probe(struct platform_device *pdev) |
732 | { |
733 | struct fwnode_handle *fwnode = NULL; |
734 | struct pmc_usb *pmc; |
735 | int i = 0; |
736 | int ret; |
737 | |
738 | pmc = devm_kzalloc(dev: &pdev->dev, size: sizeof(*pmc), GFP_KERNEL); |
739 | if (!pmc) |
740 | return -ENOMEM; |
741 | |
742 | device_for_each_child_node(&pdev->dev, fwnode) |
743 | pmc->num_ports++; |
744 | |
745 | /* The IOM microcontroller has a limitation of max 4 ports. */ |
746 | if (pmc->num_ports > 4) { |
747 | dev_err(&pdev->dev, "driver limited to 4 ports\n" ); |
748 | return -ERANGE; |
749 | } |
750 | |
751 | pmc->port = devm_kcalloc(dev: &pdev->dev, n: pmc->num_ports, |
752 | size: sizeof(struct pmc_usb_port), GFP_KERNEL); |
753 | if (!pmc->port) |
754 | return -ENOMEM; |
755 | |
756 | pmc->ipc = devm_intel_scu_ipc_dev_get(dev: &pdev->dev); |
757 | if (!pmc->ipc) |
758 | return -ENODEV; |
759 | |
760 | pmc->dev = &pdev->dev; |
761 | |
762 | ret = pmc_usb_probe_iom(pmc); |
763 | if (ret) |
764 | return ret; |
765 | |
766 | pmc->dentry = debugfs_create_dir(name: dev_name(dev: pmc->dev), parent: pmc_mux_debugfs_root); |
767 | |
768 | /* |
769 | * For every physical USB connector (USB2 and USB3 combo) there is a |
770 | * child ACPI device node under the PMC mux ACPI device object. |
771 | */ |
772 | for (i = 0; i < pmc->num_ports; i++) { |
773 | fwnode = device_get_next_child_node(dev: pmc->dev, child: fwnode); |
774 | if (!fwnode) |
775 | break; |
776 | |
777 | ret = pmc_usb_register_port(pmc, index: i, fwnode); |
778 | if (ret) { |
779 | fwnode_handle_put(fwnode); |
780 | goto err_remove_ports; |
781 | } |
782 | |
783 | pmc_mux_port_debugfs_init(port: &pmc->port[i]); |
784 | } |
785 | |
786 | platform_set_drvdata(pdev, data: pmc); |
787 | |
788 | return 0; |
789 | |
790 | err_remove_ports: |
791 | for (i = 0; i < pmc->num_ports; i++) { |
792 | typec_switch_unregister(sw: pmc->port[i].typec_sw); |
793 | typec_mux_unregister(mux: pmc->port[i].typec_mux); |
794 | usb_role_switch_unregister(sw: pmc->port[i].usb_sw); |
795 | } |
796 | |
797 | acpi_dev_put(adev: pmc->iom_adev); |
798 | |
799 | debugfs_remove(dentry: pmc->dentry); |
800 | |
801 | return ret; |
802 | } |
803 | |
804 | static void pmc_usb_remove(struct platform_device *pdev) |
805 | { |
806 | struct pmc_usb *pmc = platform_get_drvdata(pdev); |
807 | int i; |
808 | |
809 | for (i = 0; i < pmc->num_ports; i++) { |
810 | typec_switch_unregister(sw: pmc->port[i].typec_sw); |
811 | typec_mux_unregister(mux: pmc->port[i].typec_mux); |
812 | usb_role_switch_unregister(sw: pmc->port[i].usb_sw); |
813 | } |
814 | |
815 | acpi_dev_put(adev: pmc->iom_adev); |
816 | |
817 | debugfs_remove(dentry: pmc->dentry); |
818 | } |
819 | |
820 | static const struct acpi_device_id pmc_usb_acpi_ids[] = { |
821 | { "INTC105C" , }, |
822 | { } |
823 | }; |
824 | MODULE_DEVICE_TABLE(acpi, pmc_usb_acpi_ids); |
825 | |
826 | static struct platform_driver pmc_usb_driver = { |
827 | .driver = { |
828 | .name = "intel_pmc_usb" , |
829 | .acpi_match_table = ACPI_PTR(pmc_usb_acpi_ids), |
830 | }, |
831 | .probe = pmc_usb_probe, |
832 | .remove_new = pmc_usb_remove, |
833 | }; |
834 | |
835 | static int __init pmc_usb_init(void) |
836 | { |
837 | pmc_mux_debugfs_root = debugfs_create_dir(name: "intel_pmc_mux" , parent: usb_debug_root); |
838 | |
839 | return platform_driver_register(&pmc_usb_driver); |
840 | } |
841 | module_init(pmc_usb_init); |
842 | |
843 | static void __exit pmc_usb_exit(void) |
844 | { |
845 | platform_driver_unregister(&pmc_usb_driver); |
846 | debugfs_remove(dentry: pmc_mux_debugfs_root); |
847 | } |
848 | module_exit(pmc_usb_exit); |
849 | |
850 | MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>" ); |
851 | MODULE_LICENSE("GPL v2" ); |
852 | MODULE_DESCRIPTION("Intel PMC USB mux control" ); |
853 | |