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

usb: typec: tps6598x: Add USB role switching logic

This patch adds USB role switch support to the tps6598x.

The setup to initiate or accept a data-role switch is both assumed and
currently required to be baked-into the firmware as described in TI's
document here.

Link: https://www.ti.com/lit/an/slva843a/slva843a.pdf

With this change its possible to use the USB role-switch API to detect and
notify role-switches to downstream consumers.

Tested with a ChipIdea controller on a Qualcomm MSM8939.

Cc: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Nikolaus Voss <nikolaus.voss@loewensteinmedical.de>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: Gustavo A. R. Silva <garsilva@embeddedor.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: linux-usb@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
Link: https://lore.kernel.org/r/20200511231930.2825183-2-bryan.odonoghue@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Bryan O'Donoghue and committed by
Greg Kroah-Hartman
18a6c866 0ef1f6e3

+50 -7
+50 -7
drivers/usb/typec/tps6598x.c
··· 12 12 #include <linux/regmap.h> 13 13 #include <linux/interrupt.h> 14 14 #include <linux/usb/typec.h> 15 + #include <linux/usb/role.h> 15 16 16 17 /* Register offsets */ 17 18 #define TPS_REG_VID 0x00 ··· 95 94 struct typec_port *port; 96 95 struct typec_partner *partner; 97 96 struct usb_pd_identity partner_identity; 97 + struct usb_role_switch *role_sw; 98 98 }; 99 99 100 100 /* ··· 192 190 return 0; 193 191 } 194 192 193 + static void tps6598x_set_data_role(struct tps6598x *tps, 194 + enum typec_data_role role, bool connected) 195 + { 196 + enum usb_role role_val; 197 + 198 + if (role == TYPEC_HOST) 199 + role_val = USB_ROLE_HOST; 200 + else 201 + role_val = USB_ROLE_DEVICE; 202 + 203 + if (!connected) 204 + role_val = USB_ROLE_NONE; 205 + 206 + usb_role_switch_set_role(tps->role_sw, role_val); 207 + typec_set_data_role(tps->port, role); 208 + } 209 + 195 210 static int tps6598x_connect(struct tps6598x *tps, u32 status) 196 211 { 197 212 struct typec_partner_desc desc; ··· 239 220 typec_set_pwr_opmode(tps->port, mode); 240 221 typec_set_pwr_role(tps->port, TPS_STATUS_PORTROLE(status)); 241 222 typec_set_vconn_role(tps->port, TPS_STATUS_VCONN(status)); 242 - typec_set_data_role(tps->port, TPS_STATUS_DATAROLE(status)); 223 + tps6598x_set_data_role(tps, TPS_STATUS_DATAROLE(status), true); 243 224 244 225 tps->partner = typec_register_partner(tps->port, &desc); 245 226 if (IS_ERR(tps->partner)) ··· 259 240 typec_set_pwr_opmode(tps->port, TYPEC_PWR_MODE_USB); 260 241 typec_set_pwr_role(tps->port, TPS_STATUS_PORTROLE(status)); 261 242 typec_set_vconn_role(tps->port, TPS_STATUS_VCONN(status)); 262 - typec_set_data_role(tps->port, TPS_STATUS_DATAROLE(status)); 243 + tps6598x_set_data_role(tps, TPS_STATUS_DATAROLE(status), false); 263 244 } 264 245 265 246 static int tps6598x_exec_cmd(struct tps6598x *tps, const char *cmd, ··· 347 328 goto out_unlock; 348 329 } 349 330 350 - typec_set_data_role(tps->port, role); 331 + tps6598x_set_data_role(tps, role, true); 351 332 352 333 out_unlock: 353 334 mutex_unlock(&tps->lock); ··· 471 452 { 472 453 struct typec_capability typec_cap = { }; 473 454 struct tps6598x *tps; 455 + struct fwnode_handle *fwnode; 474 456 u32 status; 475 457 u32 conf; 476 458 u32 vid; ··· 515 495 if (ret < 0) 516 496 return ret; 517 497 498 + fwnode = device_get_named_child_node(&client->dev, "connector"); 499 + if (IS_ERR(fwnode)) 500 + return PTR_ERR(fwnode); 501 + 502 + tps->role_sw = fwnode_usb_role_switch_get(fwnode); 503 + if (IS_ERR(tps->role_sw)) { 504 + ret = PTR_ERR(tps->role_sw); 505 + goto err_fwnode_put; 506 + } 507 + 518 508 typec_cap.revision = USB_TYPEC_REV_1_2; 519 509 typec_cap.pd_revision = 0x200; 520 510 typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; 521 511 typec_cap.driver_data = tps; 522 512 typec_cap.ops = &tps6598x_ops; 513 + typec_cap.fwnode = fwnode; 523 514 524 515 switch (TPS_SYSCONF_PORTINFO(conf)) { 525 516 case TPS_PORTINFO_SINK_ACCESSORY: ··· 556 525 typec_cap.data = TYPEC_PORT_DFP; 557 526 break; 558 527 default: 559 - return -ENODEV; 528 + ret = -ENODEV; 529 + goto err_role_put; 560 530 } 561 531 562 532 tps->port = typec_register_port(&client->dev, &typec_cap); 563 - if (IS_ERR(tps->port)) 564 - return PTR_ERR(tps->port); 533 + if (IS_ERR(tps->port)) { 534 + ret = PTR_ERR(tps->port); 535 + goto err_role_put; 536 + } 537 + fwnode_handle_put(fwnode); 565 538 566 539 if (status & TPS_STATUS_PLUG_PRESENT) { 567 540 ret = tps6598x_connect(tps, status); ··· 580 545 if (ret) { 581 546 tps6598x_disconnect(tps, 0); 582 547 typec_unregister_port(tps->port); 583 - return ret; 548 + goto err_role_put; 584 549 } 585 550 586 551 i2c_set_clientdata(client, tps); 587 552 588 553 return 0; 554 + 555 + err_role_put: 556 + usb_role_switch_put(tps->role_sw); 557 + err_fwnode_put: 558 + fwnode_handle_put(fwnode); 559 + 560 + return ret; 589 561 } 590 562 591 563 static int tps6598x_remove(struct i2c_client *client) ··· 601 559 602 560 tps6598x_disconnect(tps, 0); 603 561 typec_unregister_port(tps->port); 562 + usb_role_switch_put(tps->role_sw); 604 563 605 564 return 0; 606 565 }