Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: typec: driver for Intel PMC mux control

The Intel PMC microcontroller on the latest Intel platforms
has a new function that allows configuration of the USB
Multiplexer/DeMultiplexer switches that are under the
control of the PMC.

The Intel PMC mux control (aka. mux-agent) can be used for
swapping the USB data role and for entering alternate modes,
DisplayPort or Thunderbolt3.

Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Link: https://lore.kernel.org/r/20200302135353.56659-10-heikki.krogerus@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Heikki Krogerus and committed by
Greg Kroah-Hartman
6701adfa ca469c29

+444
+9
drivers/usb/typec/mux/Kconfig
··· 9 9 Say Y or M if your system has a Pericom PI3USB30532 Type-C cross 10 10 switch / mux chip found on some devices with a Type-C port. 11 11 12 + config TYPEC_MUX_INTEL_PMC 13 + tristate "Intel PMC mux control" 14 + depends on INTEL_PMC_IPC 15 + select USB_ROLE_SWITCH 16 + help 17 + Driver for USB muxes controlled by Intel PMC FW. Intel PMC FW can 18 + control the USB role switch and also the multiplexer/demultiplexer 19 + switches used with USB Type-C Alternate Modes. 20 + 12 21 endmenu
+1
drivers/usb/typec/mux/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 2 3 3 obj-$(CONFIG_TYPEC_MUX_PI3USB30532) += pi3usb30532.o 4 + obj-$(CONFIG_TYPEC_MUX_INTEL_PMC) += intel_pmc_mux.o
+434
drivers/usb/typec/mux/intel_pmc_mux.c
··· 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/role.h> 14 + #include <linux/usb/typec_mux.h> 15 + #include <linux/usb/typec_dp.h> 16 + #include <linux/usb/typec_tbt.h> 17 + 18 + #include <asm/intel_pmc_ipc.h> 19 + 20 + #define PMC_USBC_CMD 0xa7 21 + 22 + /* "Usage" OOB Message field values */ 23 + enum { 24 + PMC_USB_CONNECT, 25 + PMC_USB_DISCONNECT, 26 + PMC_USB_SAFE_MODE, 27 + PMC_USB_ALT_MODE, 28 + PMC_USB_DP_HPD, 29 + }; 30 + 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 36 + 37 + /* Alt Mode Request */ 38 + struct altmode_req { 39 + u8 usage; 40 + u8 mode_type; 41 + u8 mode_id; 42 + u8 reserved; 43 + u32 mode_data; 44 + } __packed; 45 + 46 + #define PMC_USB_MODE_TYPE_SHIFT 4 47 + 48 + enum { 49 + PMC_USB_MODE_TYPE_USB, 50 + PMC_USB_MODE_TYPE_DP, 51 + PMC_USB_MODE_TYPE_TBT, 52 + }; 53 + 54 + /* Common Mode Data bits */ 55 + #define PMC_USB_ALTMODE_ACTIVE_CABLE BIT(2) 56 + 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 61 + 62 + /* DP specific Mode Data bits */ 63 + #define PMC_USB_ALTMODE_DP_MODE_SHIFT 8 64 + 65 + /* TBT specific Mode Data bits */ 66 + #define PMC_USB_ALTMODE_TBT_TYPE BIT(17) 67 + #define PMC_USB_ALTMODE_CABLE_TYPE BIT(18) 68 + #define PMC_USB_ALTMODE_ACTIVE_LINK BIT(20) 69 + #define PMC_USB_ALTMODE_FORCE_LSR BIT(23) 70 + #define PMC_USB_ALTMODE_CABLE_SPD(_s_) (((_s_) & GENMASK(2, 0)) << 25) 71 + #define PMC_USB_ALTMODE_CABLE_USB31 1 72 + #define PMC_USB_ALTMODE_CABLE_10GPS 2 73 + #define PMC_USB_ALTMODE_CABLE_20GPS 3 74 + #define PMC_USB_ALTMODE_TBT_GEN(_g_) (((_g_) & GENMASK(1, 0)) << 28) 75 + 76 + /* Display HPD Request bits */ 77 + #define PMC_USB_DP_HPD_IRQ BIT(5) 78 + #define PMC_USB_DP_HPD_LVL BIT(6) 79 + 80 + struct pmc_usb; 81 + 82 + struct pmc_usb_port { 83 + int num; 84 + struct pmc_usb *pmc; 85 + struct typec_mux *typec_mux; 86 + struct typec_switch *typec_sw; 87 + struct usb_role_switch *usb_sw; 88 + 89 + enum typec_orientation orientation; 90 + enum usb_role role; 91 + 92 + u8 usb2_port; 93 + u8 usb3_port; 94 + }; 95 + 96 + struct pmc_usb { 97 + u8 num_ports; 98 + struct device *dev; 99 + struct pmc_usb_port *port; 100 + }; 101 + 102 + static int pmc_usb_command(struct pmc_usb_port *port, u8 *msg, u32 len) 103 + { 104 + u8 response[4]; 105 + 106 + /* 107 + * Error bit will always be 0 with the USBC command. 108 + * Status can be checked from the response message. 109 + */ 110 + intel_pmc_ipc_command(PMC_USBC_CMD, 0, msg, len, 111 + (void *)response, 1); 112 + 113 + if (response[2]) { 114 + if (response[2] & BIT(1)) 115 + return -EIO; 116 + return -EBUSY; 117 + } 118 + 119 + return 0; 120 + } 121 + 122 + static int 123 + pmc_usb_mux_dp_hpd(struct pmc_usb_port *port, struct typec_mux_state *state) 124 + { 125 + struct typec_displayport_data *data = state->data; 126 + u8 msg[2] = { }; 127 + 128 + msg[0] = PMC_USB_DP_HPD; 129 + msg[0] |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT; 130 + 131 + msg[1] = PMC_USB_DP_HPD_IRQ; 132 + 133 + if (data->status & DP_STATUS_HPD_STATE) 134 + msg[1] |= PMC_USB_DP_HPD_LVL; 135 + 136 + return pmc_usb_command(port, msg, sizeof(msg)); 137 + } 138 + 139 + static int 140 + pmc_usb_mux_dp(struct pmc_usb_port *port, struct typec_mux_state *state) 141 + { 142 + struct typec_displayport_data *data = state->data; 143 + struct altmode_req req = { }; 144 + 145 + if (data->status & DP_STATUS_IRQ_HPD) 146 + return pmc_usb_mux_dp_hpd(port, state); 147 + 148 + req.usage = PMC_USB_ALT_MODE; 149 + req.usage |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT; 150 + req.mode_type = PMC_USB_MODE_TYPE_DP << PMC_USB_MODE_TYPE_SHIFT; 151 + 152 + req.mode_data = (port->orientation - 1) << PMC_USB_ALTMODE_ORI_SHIFT; 153 + req.mode_data |= (port->role - 1) << PMC_USB_ALTMODE_UFP_SHIFT; 154 + req.mode_data |= (port->orientation - 1) << PMC_USB_ALTMODE_ORI_AUX_SHIFT; 155 + req.mode_data |= (port->orientation - 1) << PMC_USB_ALTMODE_ORI_HSL_SHIFT; 156 + 157 + req.mode_data |= (state->mode - TYPEC_STATE_MODAL) << 158 + PMC_USB_ALTMODE_DP_MODE_SHIFT; 159 + 160 + return pmc_usb_command(port, (void *)&req, sizeof(req)); 161 + } 162 + 163 + static int 164 + pmc_usb_mux_tbt(struct pmc_usb_port *port, struct typec_mux_state *state) 165 + { 166 + struct typec_thunderbolt_data *data = state->data; 167 + u8 cable_speed = TBT_CABLE_SPEED(data->cable_mode); 168 + struct altmode_req req = { }; 169 + 170 + req.usage = PMC_USB_ALT_MODE; 171 + req.usage |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT; 172 + req.mode_type = PMC_USB_MODE_TYPE_TBT << PMC_USB_MODE_TYPE_SHIFT; 173 + 174 + req.mode_data = (port->orientation - 1) << PMC_USB_ALTMODE_ORI_SHIFT; 175 + req.mode_data |= (port->role - 1) << PMC_USB_ALTMODE_UFP_SHIFT; 176 + req.mode_data |= (port->orientation - 1) << PMC_USB_ALTMODE_ORI_AUX_SHIFT; 177 + req.mode_data |= (port->orientation - 1) << PMC_USB_ALTMODE_ORI_HSL_SHIFT; 178 + 179 + if (TBT_ADAPTER(data->device_mode) == TBT_ADAPTER_TBT3) 180 + req.mode_data |= PMC_USB_ALTMODE_TBT_TYPE; 181 + 182 + if (data->cable_mode & TBT_CABLE_OPTICAL) 183 + req.mode_data |= PMC_USB_ALTMODE_CABLE_TYPE; 184 + 185 + if (data->cable_mode & TBT_CABLE_LINK_TRAINING) 186 + req.mode_data |= PMC_USB_ALTMODE_ACTIVE_LINK; 187 + 188 + if (data->enter_vdo & TBT_ENTER_MODE_ACTIVE_CABLE) 189 + req.mode_data |= PMC_USB_ALTMODE_ACTIVE_CABLE; 190 + 191 + req.mode_data |= PMC_USB_ALTMODE_CABLE_SPD(cable_speed); 192 + 193 + return pmc_usb_command(port, (void *)&req, sizeof(req)); 194 + } 195 + 196 + static int pmc_usb_mux_safe_state(struct pmc_usb_port *port) 197 + { 198 + u8 msg; 199 + 200 + msg = PMC_USB_SAFE_MODE; 201 + msg |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT; 202 + 203 + return pmc_usb_command(port, &msg, sizeof(msg)); 204 + } 205 + 206 + static int pmc_usb_connect(struct pmc_usb_port *port) 207 + { 208 + u8 msg[2]; 209 + 210 + msg[0] = PMC_USB_CONNECT; 211 + msg[0] |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT; 212 + 213 + msg[1] = port->usb2_port << PMC_USB_MSG_USB2_PORT_SHIFT; 214 + msg[1] |= (port->orientation - 1) << PMC_USB_MSG_ORI_HSL_SHIFT; 215 + msg[1] |= (port->orientation - 1) << PMC_USB_MSG_ORI_AUX_SHIFT; 216 + 217 + return pmc_usb_command(port, msg, sizeof(msg)); 218 + } 219 + 220 + static int pmc_usb_disconnect(struct pmc_usb_port *port) 221 + { 222 + u8 msg[2]; 223 + 224 + msg[0] = PMC_USB_DISCONNECT; 225 + msg[0] |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT; 226 + 227 + msg[1] = port->usb2_port << PMC_USB_MSG_USB2_PORT_SHIFT; 228 + 229 + return pmc_usb_command(port, msg, sizeof(msg)); 230 + } 231 + 232 + static int 233 + pmc_usb_mux_set(struct typec_mux *mux, struct typec_mux_state *state) 234 + { 235 + struct pmc_usb_port *port = typec_mux_get_drvdata(mux); 236 + 237 + if (!state->alt) 238 + return 0; 239 + 240 + if (state->mode == TYPEC_STATE_SAFE) 241 + return pmc_usb_mux_safe_state(port); 242 + 243 + switch (state->alt->svid) { 244 + case USB_TYPEC_TBT_SID: 245 + return pmc_usb_mux_tbt(port, state); 246 + case USB_TYPEC_DP_SID: 247 + return pmc_usb_mux_dp(port, state); 248 + } 249 + 250 + return -EOPNOTSUPP; 251 + } 252 + 253 + static int pmc_usb_set_orientation(struct typec_switch *sw, 254 + enum typec_orientation orientation) 255 + { 256 + struct pmc_usb_port *port = typec_switch_get_drvdata(sw); 257 + 258 + if (port->orientation == orientation) 259 + return 0; 260 + 261 + port->orientation = orientation; 262 + 263 + if (port->role) { 264 + if (orientation == TYPEC_ORIENTATION_NONE) 265 + return pmc_usb_disconnect(port); 266 + else 267 + return pmc_usb_connect(port); 268 + } 269 + 270 + return 0; 271 + } 272 + 273 + static int pmc_usb_set_role(struct usb_role_switch *sw, enum usb_role role) 274 + { 275 + struct pmc_usb_port *port = usb_role_switch_get_drvdata(sw); 276 + 277 + if (port->role == role) 278 + return 0; 279 + 280 + port->role = role; 281 + 282 + if (port->orientation) { 283 + if (role == USB_ROLE_NONE) 284 + return pmc_usb_disconnect(port); 285 + else 286 + return pmc_usb_connect(port); 287 + } 288 + 289 + return 0; 290 + } 291 + 292 + static int pmc_usb_register_port(struct pmc_usb *pmc, int index, 293 + struct fwnode_handle *fwnode) 294 + { 295 + struct pmc_usb_port *port = &pmc->port[index]; 296 + struct usb_role_switch_desc desc = { }; 297 + struct typec_switch_desc sw_desc = { }; 298 + struct typec_mux_desc mux_desc = { }; 299 + int ret; 300 + 301 + ret = fwnode_property_read_u8(fwnode, "usb2-port", &port->usb2_port); 302 + if (ret) 303 + return ret; 304 + 305 + ret = fwnode_property_read_u8(fwnode, "usb3-port", &port->usb3_port); 306 + if (ret) 307 + return ret; 308 + 309 + port->num = index; 310 + port->pmc = pmc; 311 + 312 + sw_desc.fwnode = fwnode; 313 + sw_desc.drvdata = port; 314 + sw_desc.name = fwnode_get_name(fwnode); 315 + sw_desc.set = pmc_usb_set_orientation; 316 + 317 + port->typec_sw = typec_switch_register(pmc->dev, &sw_desc); 318 + if (IS_ERR(port->typec_sw)) 319 + return PTR_ERR(port->typec_sw); 320 + 321 + mux_desc.fwnode = fwnode; 322 + mux_desc.drvdata = port; 323 + mux_desc.name = fwnode_get_name(fwnode); 324 + mux_desc.set = pmc_usb_mux_set; 325 + 326 + port->typec_mux = typec_mux_register(pmc->dev, &mux_desc); 327 + if (IS_ERR(port->typec_mux)) { 328 + ret = PTR_ERR(port->typec_mux); 329 + goto err_unregister_switch; 330 + } 331 + 332 + desc.fwnode = fwnode; 333 + desc.driver_data = port; 334 + desc.name = fwnode_get_name(fwnode); 335 + desc.set = pmc_usb_set_role; 336 + 337 + port->usb_sw = usb_role_switch_register(pmc->dev, &desc); 338 + if (IS_ERR(port->usb_sw)) { 339 + ret = PTR_ERR(port->usb_sw); 340 + goto err_unregister_mux; 341 + } 342 + 343 + return 0; 344 + 345 + err_unregister_mux: 346 + typec_mux_unregister(port->typec_mux); 347 + 348 + err_unregister_switch: 349 + typec_switch_unregister(port->typec_sw); 350 + 351 + return ret; 352 + } 353 + 354 + static int pmc_usb_probe(struct platform_device *pdev) 355 + { 356 + struct fwnode_handle *fwnode = NULL; 357 + struct pmc_usb *pmc; 358 + int i = 0; 359 + int ret; 360 + 361 + pmc = devm_kzalloc(&pdev->dev, sizeof(*pmc), GFP_KERNEL); 362 + if (!pmc) 363 + return -ENOMEM; 364 + 365 + device_for_each_child_node(&pdev->dev, fwnode) 366 + pmc->num_ports++; 367 + 368 + pmc->port = devm_kcalloc(&pdev->dev, pmc->num_ports, 369 + sizeof(struct pmc_usb_port), GFP_KERNEL); 370 + if (!pmc->port) 371 + return -ENOMEM; 372 + 373 + pmc->dev = &pdev->dev; 374 + 375 + /* 376 + * For every physical USB connector (USB2 and USB3 combo) there is a 377 + * child ACPI device node under the PMC mux ACPI device object. 378 + */ 379 + for (i = 0; i < pmc->num_ports; i++) { 380 + fwnode = device_get_next_child_node(pmc->dev, fwnode); 381 + if (!fwnode) 382 + break; 383 + 384 + ret = pmc_usb_register_port(pmc, i, fwnode); 385 + if (ret) 386 + goto err_remove_ports; 387 + } 388 + 389 + platform_set_drvdata(pdev, pmc); 390 + 391 + return 0; 392 + 393 + err_remove_ports: 394 + for (i = 0; i < pmc->num_ports; i++) { 395 + typec_switch_unregister(pmc->port[i].typec_sw); 396 + typec_mux_unregister(pmc->port[i].typec_mux); 397 + } 398 + 399 + return ret; 400 + } 401 + 402 + static int pmc_usb_remove(struct platform_device *pdev) 403 + { 404 + struct pmc_usb *pmc = platform_get_drvdata(pdev); 405 + int i; 406 + 407 + for (i = 0; i < pmc->num_ports; i++) { 408 + typec_switch_unregister(pmc->port[i].typec_sw); 409 + typec_mux_unregister(pmc->port[i].typec_mux); 410 + } 411 + 412 + return 0; 413 + } 414 + 415 + static const struct acpi_device_id pmc_usb_acpi_ids[] = { 416 + { "INTC105C", }, 417 + { } 418 + }; 419 + MODULE_DEVICE_TABLE(acpi, pmc_usb_acpi_ids); 420 + 421 + static struct platform_driver pmc_usb_driver = { 422 + .driver = { 423 + .name = "intel_pmc_usb", 424 + .acpi_match_table = ACPI_PTR(pmc_usb_acpi_ids), 425 + }, 426 + .probe = pmc_usb_probe, 427 + .remove = pmc_usb_remove, 428 + }; 429 + 430 + module_platform_driver(pmc_usb_driver); 431 + 432 + MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>"); 433 + MODULE_LICENSE("GPL v2"); 434 + MODULE_DESCRIPTION("Intel PMC USB mux control");