1 // SPDX-License-Identifier: GPL-2.0 2 // ChromeOS Embedded Controller extcon 3 // 4 // Copyright (C) 2017 Google, Inc. 5 // Author: Benson Leung <bleung@chromium.org> 6 7 #include <linux/extcon-provider.h> 8 #include <linux/kernel.h> 9 #include <linux/mfd/cros_ec.h> 10 #include <linux/module.h> 11 #include <linux/notifier.h> 12 #include <linux/of.h> 13 #include <linux/platform_device.h> 14 #include <linux/slab.h> 15 #include <linux/sched.h> 16 17 struct cros_ec_extcon_info { 18 struct device *dev; 19 struct extcon_dev *edev; 20 21 int port_id; 22 23 struct cros_ec_device *ec; 24 25 struct notifier_block notifier; 26 27 unsigned int dr; /* data role */ 28 bool pr; /* power role (true if VBUS enabled) */ 29 bool dp; /* DisplayPort enabled */ 30 bool mux; /* SuperSpeed (usb3) enabled */ 31 unsigned int power_type; 32 }; 33 34 static const unsigned int usb_type_c_cable[] = { 35 EXTCON_USB, 36 EXTCON_USB_HOST, 37 EXTCON_DISP_DP, 38 EXTCON_NONE, 39 }; 40 41 enum usb_data_roles { 42 DR_NONE, 43 DR_HOST, 44 DR_DEVICE, 45 }; 46 47 /** 48 * cros_ec_pd_command() - Send a command to the EC. 49 * @info: pointer to struct cros_ec_extcon_info 50 * @command: EC command 51 * @version: EC command version 52 * @outdata: EC command output data 53 * @outsize: Size of outdata 54 * @indata: EC command input data 55 * @insize: Size of indata 56 * 57 * Return: 0 on success, <0 on failure. 58 */ 59 static int cros_ec_pd_command(struct cros_ec_extcon_info *info, 60 unsigned int command, 61 unsigned int version, 62 void *outdata, 63 unsigned int outsize, 64 void *indata, 65 unsigned int insize) 66 { 67 struct cros_ec_command *msg; 68 int ret; 69 70 msg = kzalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL); 71 if (!msg) 72 return -ENOMEM; 73 74 msg->version = version; 75 msg->command = command; 76 msg->outsize = outsize; 77 msg->insize = insize; 78 79 if (outsize) 80 memcpy(msg->data, outdata, outsize); 81 82 ret = cros_ec_cmd_xfer_status(info->ec, msg); 83 if (ret >= 0 && insize) 84 memcpy(indata, msg->data, insize); 85 86 kfree(msg); 87 return ret; 88 } 89 90 /** 91 * cros_ec_usb_get_power_type() - Get power type info about PD device attached 92 * to given port. 93 * @info: pointer to struct cros_ec_extcon_info 94 * 95 * Return: power type on success, <0 on failure. 96 */ 97 static int cros_ec_usb_get_power_type(struct cros_ec_extcon_info *info) 98 { 99 struct ec_params_usb_pd_power_info req; 100 struct ec_response_usb_pd_power_info resp; 101 int ret; 102 103 req.port = info->port_id; 104 ret = cros_ec_pd_command(info, EC_CMD_USB_PD_POWER_INFO, 0, 105 &req, sizeof(req), &resp, sizeof(resp)); 106 if (ret < 0) 107 return ret; 108 109 return resp.type; 110 } 111 112 /** 113 * cros_ec_usb_get_pd_mux_state() - Get PD mux state for given port. 114 * @info: pointer to struct cros_ec_extcon_info 115 * 116 * Return: PD mux state on success, <0 on failure. 117 */ 118 static int cros_ec_usb_get_pd_mux_state(struct cros_ec_extcon_info *info) 119 { 120 struct ec_params_usb_pd_mux_info req; 121 struct ec_response_usb_pd_mux_info resp; 122 int ret; 123 124 req.port = info->port_id; 125 ret = cros_ec_pd_command(info, EC_CMD_USB_PD_MUX_INFO, 0, 126 &req, sizeof(req), 127 &resp, sizeof(resp)); 128 if (ret < 0) 129 return ret; 130 131 return resp.flags; 132 } 133 134 /** 135 * cros_ec_usb_get_role() - Get role info about possible PD device attached to a 136 * given port. 137 * @info: pointer to struct cros_ec_extcon_info 138 * @polarity: pointer to cable polarity (return value) 139 * 140 * Return: role info on success, -ENOTCONN if no cable is connected, <0 on 141 * failure. 142 */ 143 static int cros_ec_usb_get_role(struct cros_ec_extcon_info *info, 144 bool *polarity) 145 { 146 struct ec_params_usb_pd_control pd_control; 147 struct ec_response_usb_pd_control_v1 resp; 148 int ret; 149 150 pd_control.port = info->port_id; 151 pd_control.role = USB_PD_CTRL_ROLE_NO_CHANGE; 152 pd_control.mux = USB_PD_CTRL_MUX_NO_CHANGE; 153 pd_control.swap = USB_PD_CTRL_SWAP_NONE; 154 ret = cros_ec_pd_command(info, EC_CMD_USB_PD_CONTROL, 1, 155 &pd_control, sizeof(pd_control), 156 &resp, sizeof(resp)); 157 if (ret < 0) 158 return ret; 159 160 if (!(resp.enabled & PD_CTRL_RESP_ENABLED_CONNECTED)) 161 return -ENOTCONN; 162 163 *polarity = resp.polarity; 164 165 return resp.role; 166 } 167 168 /** 169 * cros_ec_pd_get_num_ports() - Get number of EC charge ports. 170 * @info: pointer to struct cros_ec_extcon_info 171 * 172 * Return: number of ports on success, <0 on failure. 173 */ 174 static int cros_ec_pd_get_num_ports(struct cros_ec_extcon_info *info) 175 { 176 struct ec_response_usb_pd_ports resp; 177 int ret; 178 179 ret = cros_ec_pd_command(info, EC_CMD_USB_PD_PORTS, 180 0, NULL, 0, &resp, sizeof(resp)); 181 if (ret < 0) 182 return ret; 183 184 return resp.num_ports; 185 } 186 187 static const char *cros_ec_usb_role_string(unsigned int role) 188 { 189 return role == DR_NONE ? "DISCONNECTED" : 190 (role == DR_HOST ? "DFP" : "UFP"); 191 } 192 193 static const char *cros_ec_usb_power_type_string(unsigned int type) 194 { 195 switch (type) { 196 case USB_CHG_TYPE_NONE: 197 return "USB_CHG_TYPE_NONE"; 198 case USB_CHG_TYPE_PD: 199 return "USB_CHG_TYPE_PD"; 200 case USB_CHG_TYPE_PROPRIETARY: 201 return "USB_CHG_TYPE_PROPRIETARY"; 202 case USB_CHG_TYPE_C: 203 return "USB_CHG_TYPE_C"; 204 case USB_CHG_TYPE_BC12_DCP: 205 return "USB_CHG_TYPE_BC12_DCP"; 206 case USB_CHG_TYPE_BC12_CDP: 207 return "USB_CHG_TYPE_BC12_CDP"; 208 case USB_CHG_TYPE_BC12_SDP: 209 return "USB_CHG_TYPE_BC12_SDP"; 210 case USB_CHG_TYPE_OTHER: 211 return "USB_CHG_TYPE_OTHER"; 212 case USB_CHG_TYPE_VBUS: 213 return "USB_CHG_TYPE_VBUS"; 214 case USB_CHG_TYPE_UNKNOWN: 215 return "USB_CHG_TYPE_UNKNOWN"; 216 default: 217 return "USB_CHG_TYPE_UNKNOWN"; 218 } 219 } 220 221 static bool cros_ec_usb_power_type_is_wall_wart(unsigned int type, 222 unsigned int role) 223 { 224 switch (type) { 225 /* FIXME : Guppy, Donnettes, and other chargers will be miscategorized 226 * because they identify with USB_CHG_TYPE_C, but we can't return true 227 * here from that code because that breaks Suzy-Q and other kinds of 228 * USB Type-C cables and peripherals. 229 */ 230 case USB_CHG_TYPE_PROPRIETARY: 231 case USB_CHG_TYPE_BC12_DCP: 232 return true; 233 case USB_CHG_TYPE_PD: 234 case USB_CHG_TYPE_C: 235 case USB_CHG_TYPE_BC12_CDP: 236 case USB_CHG_TYPE_BC12_SDP: 237 case USB_CHG_TYPE_OTHER: 238 case USB_CHG_TYPE_VBUS: 239 case USB_CHG_TYPE_UNKNOWN: 240 case USB_CHG_TYPE_NONE: 241 default: 242 return false; 243 } 244 } 245 246 static int extcon_cros_ec_detect_cable(struct cros_ec_extcon_info *info, 247 bool force) 248 { 249 struct device *dev = info->dev; 250 int role, power_type; 251 unsigned int dr = DR_NONE; 252 bool pr = false; 253 bool polarity = false; 254 bool dp = false; 255 bool mux = false; 256 bool hpd = false; 257 258 power_type = cros_ec_usb_get_power_type(info); 259 if (power_type < 0) { 260 dev_err(dev, "failed getting power type err = %d\n", 261 power_type); 262 return power_type; 263 } 264 265 role = cros_ec_usb_get_role(info, &polarity); 266 if (role < 0) { 267 if (role != -ENOTCONN) { 268 dev_err(dev, "failed getting role err = %d\n", role); 269 return role; 270 } 271 dev_dbg(dev, "disconnected\n"); 272 } else { 273 int pd_mux_state; 274 275 dr = (role & PD_CTRL_RESP_ROLE_DATA) ? DR_HOST : DR_DEVICE; 276 pr = (role & PD_CTRL_RESP_ROLE_POWER); 277 pd_mux_state = cros_ec_usb_get_pd_mux_state(info); 278 if (pd_mux_state < 0) 279 pd_mux_state = USB_PD_MUX_USB_ENABLED; 280 281 dp = pd_mux_state & USB_PD_MUX_DP_ENABLED; 282 mux = pd_mux_state & USB_PD_MUX_USB_ENABLED; 283 hpd = pd_mux_state & USB_PD_MUX_HPD_IRQ; 284 285 dev_dbg(dev, 286 "connected role 0x%x pwr type %d dr %d pr %d pol %d mux %d dp %d hpd %d\n", 287 role, power_type, dr, pr, polarity, mux, dp, hpd); 288 } 289 290 /* 291 * When there is no USB host (e.g. USB PD charger), 292 * we are not really a UFP for the AP. 293 */ 294 if (dr == DR_DEVICE && 295 cros_ec_usb_power_type_is_wall_wart(power_type, role)) 296 dr = DR_NONE; 297 298 if (force || info->dr != dr || info->pr != pr || info->dp != dp || 299 info->mux != mux || info->power_type != power_type) { 300 bool host_connected = false, device_connected = false; 301 302 dev_dbg(dev, "Type/Role switch! type = %s role = %s\n", 303 cros_ec_usb_power_type_string(power_type), 304 cros_ec_usb_role_string(dr)); 305 info->dr = dr; 306 info->pr = pr; 307 info->dp = dp; 308 info->mux = mux; 309 info->power_type = power_type; 310 311 if (dr == DR_DEVICE) 312 device_connected = true; 313 else if (dr == DR_HOST) 314 host_connected = true; 315 316 extcon_set_state(info->edev, EXTCON_USB, device_connected); 317 extcon_set_state(info->edev, EXTCON_USB_HOST, host_connected); 318 extcon_set_state(info->edev, EXTCON_DISP_DP, dp); 319 extcon_set_property(info->edev, EXTCON_USB, 320 EXTCON_PROP_USB_VBUS, 321 (union extcon_property_value)(int)pr); 322 extcon_set_property(info->edev, EXTCON_USB_HOST, 323 EXTCON_PROP_USB_VBUS, 324 (union extcon_property_value)(int)pr); 325 extcon_set_property(info->edev, EXTCON_USB, 326 EXTCON_PROP_USB_TYPEC_POLARITY, 327 (union extcon_property_value)(int)polarity); 328 extcon_set_property(info->edev, EXTCON_USB_HOST, 329 EXTCON_PROP_USB_TYPEC_POLARITY, 330 (union extcon_property_value)(int)polarity); 331 extcon_set_property(info->edev, EXTCON_DISP_DP, 332 EXTCON_PROP_USB_TYPEC_POLARITY, 333 (union extcon_property_value)(int)polarity); 334 extcon_set_property(info->edev, EXTCON_USB, 335 EXTCON_PROP_USB_SS, 336 (union extcon_property_value)(int)mux); 337 extcon_set_property(info->edev, EXTCON_USB_HOST, 338 EXTCON_PROP_USB_SS, 339 (union extcon_property_value)(int)mux); 340 extcon_set_property(info->edev, EXTCON_DISP_DP, 341 EXTCON_PROP_USB_SS, 342 (union extcon_property_value)(int)mux); 343 extcon_set_property(info->edev, EXTCON_DISP_DP, 344 EXTCON_PROP_DISP_HPD, 345 (union extcon_property_value)(int)hpd); 346 347 extcon_sync(info->edev, EXTCON_USB); 348 extcon_sync(info->edev, EXTCON_USB_HOST); 349 extcon_sync(info->edev, EXTCON_DISP_DP); 350 351 } else if (hpd) { 352 extcon_set_property(info->edev, EXTCON_DISP_DP, 353 EXTCON_PROP_DISP_HPD, 354 (union extcon_property_value)(int)hpd); 355 extcon_sync(info->edev, EXTCON_DISP_DP); 356 } 357 358 return 0; 359 } 360 361 static int extcon_cros_ec_event(struct notifier_block *nb, 362 unsigned long queued_during_suspend, 363 void *_notify) 364 { 365 struct cros_ec_extcon_info *info; 366 struct cros_ec_device *ec; 367 u32 host_event; 368 369 info = container_of(nb, struct cros_ec_extcon_info, notifier); 370 ec = info->ec; 371 372 host_event = cros_ec_get_host_event(ec); 373 if (host_event & (EC_HOST_EVENT_MASK(EC_HOST_EVENT_PD_MCU) | 374 EC_HOST_EVENT_MASK(EC_HOST_EVENT_USB_MUX))) { 375 extcon_cros_ec_detect_cable(info, false); 376 return NOTIFY_OK; 377 } 378 379 return NOTIFY_DONE; 380 } 381 382 static int extcon_cros_ec_probe(struct platform_device *pdev) 383 { 384 struct cros_ec_extcon_info *info; 385 struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); 386 struct device *dev = &pdev->dev; 387 struct device_node *np = dev->of_node; 388 int numports, ret; 389 390 info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); 391 if (!info) 392 return -ENOMEM; 393 394 info->dev = dev; 395 info->ec = ec; 396 397 if (np) { 398 u32 port; 399 400 ret = of_property_read_u32(np, "google,usb-port-id", &port); 401 if (ret < 0) { 402 dev_err(dev, "Missing google,usb-port-id property\n"); 403 return ret; 404 } 405 info->port_id = port; 406 } else { 407 info->port_id = pdev->id; 408 } 409 410 numports = cros_ec_pd_get_num_ports(info); 411 if (numports < 0) { 412 dev_err(dev, "failed getting number of ports! ret = %d\n", 413 numports); 414 return numports; 415 } 416 417 if (info->port_id >= numports) { 418 dev_err(dev, "This system only supports %d ports\n", numports); 419 return -ENODEV; 420 } 421 422 info->edev = devm_extcon_dev_allocate(dev, usb_type_c_cable); 423 if (IS_ERR(info->edev)) { 424 dev_err(dev, "failed to allocate extcon device\n"); 425 return -ENOMEM; 426 } 427 428 ret = devm_extcon_dev_register(dev, info->edev); 429 if (ret < 0) { 430 dev_err(dev, "failed to register extcon device\n"); 431 return ret; 432 } 433 434 extcon_set_property_capability(info->edev, EXTCON_USB, 435 EXTCON_PROP_USB_VBUS); 436 extcon_set_property_capability(info->edev, EXTCON_USB_HOST, 437 EXTCON_PROP_USB_VBUS); 438 extcon_set_property_capability(info->edev, EXTCON_USB, 439 EXTCON_PROP_USB_TYPEC_POLARITY); 440 extcon_set_property_capability(info->edev, EXTCON_USB_HOST, 441 EXTCON_PROP_USB_TYPEC_POLARITY); 442 extcon_set_property_capability(info->edev, EXTCON_DISP_DP, 443 EXTCON_PROP_USB_TYPEC_POLARITY); 444 extcon_set_property_capability(info->edev, EXTCON_USB, 445 EXTCON_PROP_USB_SS); 446 extcon_set_property_capability(info->edev, EXTCON_USB_HOST, 447 EXTCON_PROP_USB_SS); 448 extcon_set_property_capability(info->edev, EXTCON_DISP_DP, 449 EXTCON_PROP_USB_SS); 450 extcon_set_property_capability(info->edev, EXTCON_DISP_DP, 451 EXTCON_PROP_DISP_HPD); 452 453 info->dr = DR_NONE; 454 info->pr = false; 455 456 platform_set_drvdata(pdev, info); 457 458 /* Get PD events from the EC */ 459 info->notifier.notifier_call = extcon_cros_ec_event; 460 ret = blocking_notifier_chain_register(&info->ec->event_notifier, 461 &info->notifier); 462 if (ret < 0) { 463 dev_err(dev, "failed to register notifier\n"); 464 return ret; 465 } 466 467 /* Perform initial detection */ 468 ret = extcon_cros_ec_detect_cable(info, true); 469 if (ret < 0) { 470 dev_err(dev, "failed to detect initial cable state\n"); 471 goto unregister_notifier; 472 } 473 474 return 0; 475 476 unregister_notifier: 477 blocking_notifier_chain_unregister(&info->ec->event_notifier, 478 &info->notifier); 479 return ret; 480 } 481 482 static int extcon_cros_ec_remove(struct platform_device *pdev) 483 { 484 struct cros_ec_extcon_info *info = platform_get_drvdata(pdev); 485 486 blocking_notifier_chain_unregister(&info->ec->event_notifier, 487 &info->notifier); 488 489 return 0; 490 } 491 492 #ifdef CONFIG_PM_SLEEP 493 static int extcon_cros_ec_suspend(struct device *dev) 494 { 495 return 0; 496 } 497 498 static int extcon_cros_ec_resume(struct device *dev) 499 { 500 int ret; 501 struct cros_ec_extcon_info *info = dev_get_drvdata(dev); 502 503 ret = extcon_cros_ec_detect_cable(info, true); 504 if (ret < 0) 505 dev_err(dev, "failed to detect cable state on resume\n"); 506 507 return 0; 508 } 509 510 static const struct dev_pm_ops extcon_cros_ec_dev_pm_ops = { 511 SET_SYSTEM_SLEEP_PM_OPS(extcon_cros_ec_suspend, extcon_cros_ec_resume) 512 }; 513 514 #define DEV_PM_OPS (&extcon_cros_ec_dev_pm_ops) 515 #else 516 #define DEV_PM_OPS NULL 517 #endif /* CONFIG_PM_SLEEP */ 518 519 #ifdef CONFIG_OF 520 static const struct of_device_id extcon_cros_ec_of_match[] = { 521 { .compatible = "google,extcon-usbc-cros-ec" }, 522 { /* sentinel */ } 523 }; 524 MODULE_DEVICE_TABLE(of, extcon_cros_ec_of_match); 525 #endif /* CONFIG_OF */ 526 527 static struct platform_driver extcon_cros_ec_driver = { 528 .driver = { 529 .name = "extcon-usbc-cros-ec", 530 .of_match_table = of_match_ptr(extcon_cros_ec_of_match), 531 .pm = DEV_PM_OPS, 532 }, 533 .remove = extcon_cros_ec_remove, 534 .probe = extcon_cros_ec_probe, 535 }; 536 537 module_platform_driver(extcon_cros_ec_driver); 538 539 MODULE_DESCRIPTION("ChromeOS Embedded Controller extcon driver"); 540 MODULE_AUTHOR("Benson Leung <bleung@chromium.org>"); 541 MODULE_LICENSE("GPL v2"); 542