1 // SPDX-License-Identifier: GPL-2.0
3 * Driver for Intel PMC USB mux control
5 * Copyright (C) 2020 Intel Corporation
6 * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
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/role.h>
14 #include <linux/usb/typec_mux.h>
15 #include <linux/usb/typec_dp.h>
16 #include <linux/usb/typec_tbt.h>
18 #include <asm/intel_scu_ipc.h>
20 #define PMC_USBC_CMD 0xa7
22 /* "Usage" OOB Message field values */
31 #define PMC_USB_MSG_USB2_PORT_SHIFT 0
32 #define PMC_USB_MSG_USB3_PORT_SHIFT 4
33 #define PMC_USB_MSG_UFP_SHIFT 4
34 #define PMC_USB_MSG_ORI_HSL_SHIFT 5
35 #define PMC_USB_MSG_ORI_AUX_SHIFT 6
37 /* Alt Mode Request */
46 #define PMC_USB_MODE_TYPE_SHIFT 4
49 PMC_USB_MODE_TYPE_USB
,
51 PMC_USB_MODE_TYPE_TBT
,
54 /* Common Mode Data bits */
55 #define PMC_USB_ALTMODE_ACTIVE_CABLE BIT(2)
57 #define PMC_USB_ALTMODE_ORI_SHIFT 1
58 #define PMC_USB_ALTMODE_UFP_SHIFT 3
59 #define PMC_USB_ALTMODE_ORI_AUX_SHIFT 4
60 #define PMC_USB_ALTMODE_ORI_HSL_SHIFT 5
62 /* DP specific Mode Data bits */
63 #define PMC_USB_ALTMODE_DP_MODE_SHIFT 8
65 /* TBT specific Mode Data bits */
66 #define PMC_USB_ALTMODE_HPD_HIGH BIT(14)
67 #define PMC_USB_ALTMODE_TBT_TYPE BIT(17)
68 #define PMC_USB_ALTMODE_CABLE_TYPE BIT(18)
69 #define PMC_USB_ALTMODE_ACTIVE_LINK BIT(20)
70 #define PMC_USB_ALTMODE_FORCE_LSR BIT(23)
71 #define PMC_USB_ALTMODE_CABLE_SPD(_s_) (((_s_) & GENMASK(2, 0)) << 25)
72 #define PMC_USB_ALTMODE_CABLE_USB31 1
73 #define PMC_USB_ALTMODE_CABLE_10GPS 2
74 #define PMC_USB_ALTMODE_CABLE_20GPS 3
75 #define PMC_USB_ALTMODE_TBT_GEN(_g_) (((_g_) & GENMASK(1, 0)) << 28)
77 /* Display HPD Request bits */
78 #define PMC_USB_DP_HPD_LVL BIT(4)
79 #define PMC_USB_DP_HPD_IRQ BIT(5)
86 struct typec_mux
*typec_mux
;
87 struct typec_switch
*typec_sw
;
88 struct usb_role_switch
*usb_sw
;
90 enum typec_orientation orientation
;
100 struct intel_scu_ipc_dev
*ipc
;
101 struct pmc_usb_port
*port
;
104 static int pmc_usb_command(struct pmc_usb_port
*port
, u8
*msg
, u32 len
)
109 * Error bit will always be 0 with the USBC command.
110 * Status can be checked from the response message.
112 intel_scu_ipc_dev_command(port
->pmc
->ipc
, PMC_USBC_CMD
, 0, msg
, len
,
113 response
, sizeof(response
));
115 if (response
[2] & BIT(1))
124 pmc_usb_mux_dp_hpd(struct pmc_usb_port
*port
, struct typec_mux_state
*state
)
126 struct typec_displayport_data
*data
= state
->data
;
129 msg
[0] = PMC_USB_DP_HPD
;
130 msg
[0] |= port
->usb3_port
<< PMC_USB_MSG_USB3_PORT_SHIFT
;
132 msg
[1] = PMC_USB_DP_HPD_IRQ
;
134 if (data
->status
& DP_STATUS_HPD_STATE
)
135 msg
[1] |= PMC_USB_DP_HPD_LVL
;
137 return pmc_usb_command(port
, msg
, sizeof(msg
));
141 pmc_usb_mux_dp(struct pmc_usb_port
*port
, struct typec_mux_state
*state
)
143 struct typec_displayport_data
*data
= state
->data
;
144 struct altmode_req req
= { };
146 if (data
->status
& DP_STATUS_IRQ_HPD
)
147 return pmc_usb_mux_dp_hpd(port
, state
);
149 req
.usage
= PMC_USB_ALT_MODE
;
150 req
.usage
|= port
->usb3_port
<< PMC_USB_MSG_USB3_PORT_SHIFT
;
151 req
.mode_type
= PMC_USB_MODE_TYPE_DP
<< PMC_USB_MODE_TYPE_SHIFT
;
153 req
.mode_data
= (port
->orientation
- 1) << PMC_USB_ALTMODE_ORI_SHIFT
;
154 req
.mode_data
|= (port
->role
- 1) << PMC_USB_ALTMODE_UFP_SHIFT
;
155 req
.mode_data
|= (port
->orientation
- 1) << PMC_USB_ALTMODE_ORI_AUX_SHIFT
;
156 req
.mode_data
|= (port
->orientation
- 1) << PMC_USB_ALTMODE_ORI_HSL_SHIFT
;
158 req
.mode_data
|= (state
->mode
- TYPEC_STATE_MODAL
) <<
159 PMC_USB_ALTMODE_DP_MODE_SHIFT
;
161 if (data
->status
& DP_STATUS_HPD_STATE
)
162 req
.mode_data
|= PMC_USB_ALTMODE_HPD_HIGH
;
164 return pmc_usb_command(port
, (void *)&req
, sizeof(req
));
168 pmc_usb_mux_tbt(struct pmc_usb_port
*port
, struct typec_mux_state
*state
)
170 struct typec_thunderbolt_data
*data
= state
->data
;
171 u8 cable_speed
= TBT_CABLE_SPEED(data
->cable_mode
);
172 struct altmode_req req
= { };
174 req
.usage
= PMC_USB_ALT_MODE
;
175 req
.usage
|= port
->usb3_port
<< PMC_USB_MSG_USB3_PORT_SHIFT
;
176 req
.mode_type
= PMC_USB_MODE_TYPE_TBT
<< PMC_USB_MODE_TYPE_SHIFT
;
178 req
.mode_data
= (port
->orientation
- 1) << PMC_USB_ALTMODE_ORI_SHIFT
;
179 req
.mode_data
|= (port
->role
- 1) << PMC_USB_ALTMODE_UFP_SHIFT
;
180 req
.mode_data
|= (port
->orientation
- 1) << PMC_USB_ALTMODE_ORI_AUX_SHIFT
;
181 req
.mode_data
|= (port
->orientation
- 1) << PMC_USB_ALTMODE_ORI_HSL_SHIFT
;
183 if (TBT_ADAPTER(data
->device_mode
) == TBT_ADAPTER_TBT3
)
184 req
.mode_data
|= PMC_USB_ALTMODE_TBT_TYPE
;
186 if (data
->cable_mode
& TBT_CABLE_OPTICAL
)
187 req
.mode_data
|= PMC_USB_ALTMODE_CABLE_TYPE
;
189 if (data
->cable_mode
& TBT_CABLE_LINK_TRAINING
)
190 req
.mode_data
|= PMC_USB_ALTMODE_ACTIVE_LINK
;
192 if (data
->enter_vdo
& TBT_ENTER_MODE_ACTIVE_CABLE
)
193 req
.mode_data
|= PMC_USB_ALTMODE_ACTIVE_CABLE
;
195 req
.mode_data
|= PMC_USB_ALTMODE_CABLE_SPD(cable_speed
);
197 return pmc_usb_command(port
, (void *)&req
, sizeof(req
));
200 static int pmc_usb_mux_safe_state(struct pmc_usb_port
*port
)
204 msg
= PMC_USB_SAFE_MODE
;
205 msg
|= port
->usb3_port
<< PMC_USB_MSG_USB3_PORT_SHIFT
;
207 return pmc_usb_command(port
, &msg
, sizeof(msg
));
210 static int pmc_usb_connect(struct pmc_usb_port
*port
)
214 msg
[0] = PMC_USB_CONNECT
;
215 msg
[0] |= port
->usb3_port
<< PMC_USB_MSG_USB3_PORT_SHIFT
;
217 msg
[1] = port
->usb2_port
<< PMC_USB_MSG_USB2_PORT_SHIFT
;
218 msg
[1] |= (port
->orientation
- 1) << PMC_USB_MSG_ORI_HSL_SHIFT
;
219 msg
[1] |= (port
->orientation
- 1) << PMC_USB_MSG_ORI_AUX_SHIFT
;
221 return pmc_usb_command(port
, msg
, sizeof(msg
));
224 static int pmc_usb_disconnect(struct pmc_usb_port
*port
)
228 msg
[0] = PMC_USB_DISCONNECT
;
229 msg
[0] |= port
->usb3_port
<< PMC_USB_MSG_USB3_PORT_SHIFT
;
231 msg
[1] = port
->usb2_port
<< PMC_USB_MSG_USB2_PORT_SHIFT
;
233 return pmc_usb_command(port
, msg
, sizeof(msg
));
237 pmc_usb_mux_set(struct typec_mux
*mux
, struct typec_mux_state
*state
)
239 struct pmc_usb_port
*port
= typec_mux_get_drvdata(mux
);
244 if (state
->mode
== TYPEC_STATE_SAFE
)
245 return pmc_usb_mux_safe_state(port
);
247 switch (state
->alt
->svid
) {
248 case USB_TYPEC_TBT_SID
:
249 return pmc_usb_mux_tbt(port
, state
);
250 case USB_TYPEC_DP_SID
:
251 return pmc_usb_mux_dp(port
, state
);
257 static int pmc_usb_set_orientation(struct typec_switch
*sw
,
258 enum typec_orientation orientation
)
260 struct pmc_usb_port
*port
= typec_switch_get_drvdata(sw
);
262 if (port
->orientation
== orientation
)
265 port
->orientation
= orientation
;
268 if (orientation
== TYPEC_ORIENTATION_NONE
)
269 return pmc_usb_disconnect(port
);
271 return pmc_usb_connect(port
);
277 static int pmc_usb_set_role(struct usb_role_switch
*sw
, enum usb_role role
)
279 struct pmc_usb_port
*port
= usb_role_switch_get_drvdata(sw
);
281 if (port
->role
== role
)
286 if (port
->orientation
) {
287 if (role
== USB_ROLE_NONE
)
288 return pmc_usb_disconnect(port
);
290 return pmc_usb_connect(port
);
296 static int pmc_usb_register_port(struct pmc_usb
*pmc
, int index
,
297 struct fwnode_handle
*fwnode
)
299 struct pmc_usb_port
*port
= &pmc
->port
[index
];
300 struct usb_role_switch_desc desc
= { };
301 struct typec_switch_desc sw_desc
= { };
302 struct typec_mux_desc mux_desc
= { };
305 ret
= fwnode_property_read_u8(fwnode
, "usb2-port-number", &port
->usb2_port
);
309 ret
= fwnode_property_read_u8(fwnode
, "usb3-port-number", &port
->usb3_port
);
316 sw_desc
.fwnode
= fwnode
;
317 sw_desc
.drvdata
= port
;
318 sw_desc
.name
= fwnode_get_name(fwnode
);
319 sw_desc
.set
= pmc_usb_set_orientation
;
321 port
->typec_sw
= typec_switch_register(pmc
->dev
, &sw_desc
);
322 if (IS_ERR(port
->typec_sw
))
323 return PTR_ERR(port
->typec_sw
);
325 mux_desc
.fwnode
= fwnode
;
326 mux_desc
.drvdata
= port
;
327 mux_desc
.name
= fwnode_get_name(fwnode
);
328 mux_desc
.set
= pmc_usb_mux_set
;
330 port
->typec_mux
= typec_mux_register(pmc
->dev
, &mux_desc
);
331 if (IS_ERR(port
->typec_mux
)) {
332 ret
= PTR_ERR(port
->typec_mux
);
333 goto err_unregister_switch
;
336 desc
.fwnode
= fwnode
;
337 desc
.driver_data
= port
;
338 desc
.name
= fwnode_get_name(fwnode
);
339 desc
.set
= pmc_usb_set_role
;
341 port
->usb_sw
= usb_role_switch_register(pmc
->dev
, &desc
);
342 if (IS_ERR(port
->usb_sw
)) {
343 ret
= PTR_ERR(port
->usb_sw
);
344 goto err_unregister_mux
;
350 typec_mux_unregister(port
->typec_mux
);
352 err_unregister_switch
:
353 typec_switch_unregister(port
->typec_sw
);
358 static int pmc_usb_probe(struct platform_device
*pdev
)
360 struct fwnode_handle
*fwnode
= NULL
;
365 pmc
= devm_kzalloc(&pdev
->dev
, sizeof(*pmc
), GFP_KERNEL
);
369 device_for_each_child_node(&pdev
->dev
, fwnode
)
372 pmc
->port
= devm_kcalloc(&pdev
->dev
, pmc
->num_ports
,
373 sizeof(struct pmc_usb_port
), GFP_KERNEL
);
377 pmc
->ipc
= devm_intel_scu_ipc_dev_get(&pdev
->dev
);
381 pmc
->dev
= &pdev
->dev
;
384 * For every physical USB connector (USB2 and USB3 combo) there is a
385 * child ACPI device node under the PMC mux ACPI device object.
387 for (i
= 0; i
< pmc
->num_ports
; i
++) {
388 fwnode
= device_get_next_child_node(pmc
->dev
, fwnode
);
392 ret
= pmc_usb_register_port(pmc
, i
, fwnode
);
394 goto err_remove_ports
;
397 platform_set_drvdata(pdev
, pmc
);
402 for (i
= 0; i
< pmc
->num_ports
; i
++) {
403 typec_switch_unregister(pmc
->port
[i
].typec_sw
);
404 typec_mux_unregister(pmc
->port
[i
].typec_mux
);
410 static int pmc_usb_remove(struct platform_device
*pdev
)
412 struct pmc_usb
*pmc
= platform_get_drvdata(pdev
);
415 for (i
= 0; i
< pmc
->num_ports
; i
++) {
416 typec_switch_unregister(pmc
->port
[i
].typec_sw
);
417 typec_mux_unregister(pmc
->port
[i
].typec_mux
);
423 static const struct acpi_device_id pmc_usb_acpi_ids
[] = {
427 MODULE_DEVICE_TABLE(acpi
, pmc_usb_acpi_ids
);
429 static struct platform_driver pmc_usb_driver
= {
431 .name
= "intel_pmc_usb",
432 .acpi_match_table
= ACPI_PTR(pmc_usb_acpi_ids
),
434 .probe
= pmc_usb_probe
,
435 .remove
= pmc_usb_remove
,
438 module_platform_driver(pmc_usb_driver
);
440 MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
441 MODULE_LICENSE("GPL v2");
442 MODULE_DESCRIPTION("Intel PMC USB mux control");