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