| .. | .. |
|---|
| 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 */ |
|---|
| 18 | +#define TPS_REG_VID 0x00 |
|---|
| 19 | +#define TPS_REG_MODE 0x03 |
|---|
| 17 | 20 | #define TPS_REG_CMD1 0x08 |
|---|
| 18 | 21 | #define TPS_REG_DATA1 0x09 |
|---|
| 19 | 22 | #define TPS_REG_INT_EVENT1 0x14 |
|---|
| .. | .. |
|---|
| 59 | 62 | struct tps6598x_rx_identity_reg { |
|---|
| 60 | 63 | u8 status; |
|---|
| 61 | 64 | struct usb_pd_identity identity; |
|---|
| 62 | | - u32 vdo[3]; |
|---|
| 63 | 65 | } __packed; |
|---|
| 64 | 66 | |
|---|
| 65 | 67 | /* Standard Task return codes */ |
|---|
| 66 | 68 | #define TPS_TASK_TIMEOUT 1 |
|---|
| 67 | 69 | #define TPS_TASK_REJECTED 3 |
|---|
| 70 | + |
|---|
| 71 | +enum { |
|---|
| 72 | + TPS_MODE_APP, |
|---|
| 73 | + TPS_MODE_BOOT, |
|---|
| 74 | + TPS_MODE_BIST, |
|---|
| 75 | + TPS_MODE_DISC, |
|---|
| 76 | +}; |
|---|
| 77 | + |
|---|
| 78 | +static const char *const modes[] = { |
|---|
| 79 | + [TPS_MODE_APP] = "APP ", |
|---|
| 80 | + [TPS_MODE_BOOT] = "BOOT", |
|---|
| 81 | + [TPS_MODE_BIST] = "BIST", |
|---|
| 82 | + [TPS_MODE_DISC] = "DISC", |
|---|
| 83 | +}; |
|---|
| 68 | 84 | |
|---|
| 69 | 85 | /* Unrecognized commands will be replaced with "!CMD" */ |
|---|
| 70 | 86 | #define INVALID_CMD(_cmd_) (_cmd_ == 0x444d4321) |
|---|
| .. | .. |
|---|
| 78 | 94 | struct typec_port *port; |
|---|
| 79 | 95 | struct typec_partner *partner; |
|---|
| 80 | 96 | struct usb_pd_identity partner_identity; |
|---|
| 81 | | - struct typec_capability typec_cap; |
|---|
| 97 | + struct usb_role_switch *role_sw; |
|---|
| 82 | 98 | }; |
|---|
| 83 | 99 | |
|---|
| 84 | 100 | /* |
|---|
| 85 | 101 | * Max data bytes for Data1, Data2, and other registers. See ch 1.3.2: |
|---|
| 86 | | - * http://www.ti.com/lit/ug/slvuan1a/slvuan1a.pdf |
|---|
| 102 | + * https://www.ti.com/lit/ug/slvuan1a/slvuan1a.pdf |
|---|
| 87 | 103 | */ |
|---|
| 88 | 104 | #define TPS_MAX_LEN 64 |
|---|
| 89 | 105 | |
|---|
| .. | .. |
|---|
| 175 | 191 | return 0; |
|---|
| 176 | 192 | } |
|---|
| 177 | 193 | |
|---|
| 194 | +static void tps6598x_set_data_role(struct tps6598x *tps, |
|---|
| 195 | + enum typec_data_role role, bool connected) |
|---|
| 196 | +{ |
|---|
| 197 | + enum usb_role role_val; |
|---|
| 198 | + |
|---|
| 199 | + if (role == TYPEC_HOST) |
|---|
| 200 | + role_val = USB_ROLE_HOST; |
|---|
| 201 | + else |
|---|
| 202 | + role_val = USB_ROLE_DEVICE; |
|---|
| 203 | + |
|---|
| 204 | + if (!connected) |
|---|
| 205 | + role_val = USB_ROLE_NONE; |
|---|
| 206 | + |
|---|
| 207 | + usb_role_switch_set_role(tps->role_sw, role_val); |
|---|
| 208 | + typec_set_data_role(tps->port, role); |
|---|
| 209 | +} |
|---|
| 210 | + |
|---|
| 178 | 211 | static int tps6598x_connect(struct tps6598x *tps, u32 status) |
|---|
| 179 | 212 | { |
|---|
| 180 | 213 | struct typec_partner_desc desc; |
|---|
| .. | .. |
|---|
| 205 | 238 | typec_set_pwr_opmode(tps->port, mode); |
|---|
| 206 | 239 | typec_set_pwr_role(tps->port, TPS_STATUS_PORTROLE(status)); |
|---|
| 207 | 240 | typec_set_vconn_role(tps->port, TPS_STATUS_VCONN(status)); |
|---|
| 208 | | - typec_set_data_role(tps->port, TPS_STATUS_DATAROLE(status)); |
|---|
| 241 | + tps6598x_set_data_role(tps, TPS_STATUS_DATAROLE(status), true); |
|---|
| 209 | 242 | |
|---|
| 210 | 243 | tps->partner = typec_register_partner(tps->port, &desc); |
|---|
| 211 | 244 | if (IS_ERR(tps->partner)) |
|---|
| .. | .. |
|---|
| 225 | 258 | typec_set_pwr_opmode(tps->port, TYPEC_PWR_MODE_USB); |
|---|
| 226 | 259 | typec_set_pwr_role(tps->port, TPS_STATUS_PORTROLE(status)); |
|---|
| 227 | 260 | typec_set_vconn_role(tps->port, TPS_STATUS_VCONN(status)); |
|---|
| 228 | | - typec_set_data_role(tps->port, TPS_STATUS_DATAROLE(status)); |
|---|
| 261 | + tps6598x_set_data_role(tps, TPS_STATUS_DATAROLE(status), false); |
|---|
| 229 | 262 | } |
|---|
| 230 | 263 | |
|---|
| 231 | 264 | static int tps6598x_exec_cmd(struct tps6598x *tps, const char *cmd, |
|---|
| .. | .. |
|---|
| 291 | 324 | return 0; |
|---|
| 292 | 325 | } |
|---|
| 293 | 326 | |
|---|
| 294 | | -static int |
|---|
| 295 | | -tps6598x_dr_set(const struct typec_capability *cap, enum typec_data_role role) |
|---|
| 327 | +static int tps6598x_dr_set(struct typec_port *port, enum typec_data_role role) |
|---|
| 296 | 328 | { |
|---|
| 297 | | - struct tps6598x *tps = container_of(cap, struct tps6598x, typec_cap); |
|---|
| 298 | 329 | const char *cmd = (role == TYPEC_DEVICE) ? "SWUF" : "SWDF"; |
|---|
| 330 | + struct tps6598x *tps = typec_get_drvdata(port); |
|---|
| 299 | 331 | u32 status; |
|---|
| 300 | 332 | int ret; |
|---|
| 301 | 333 | |
|---|
| .. | .. |
|---|
| 314 | 346 | goto out_unlock; |
|---|
| 315 | 347 | } |
|---|
| 316 | 348 | |
|---|
| 317 | | - typec_set_data_role(tps->port, role); |
|---|
| 349 | + tps6598x_set_data_role(tps, role, true); |
|---|
| 318 | 350 | |
|---|
| 319 | 351 | out_unlock: |
|---|
| 320 | 352 | mutex_unlock(&tps->lock); |
|---|
| .. | .. |
|---|
| 322 | 354 | return ret; |
|---|
| 323 | 355 | } |
|---|
| 324 | 356 | |
|---|
| 325 | | -static int |
|---|
| 326 | | -tps6598x_pr_set(const struct typec_capability *cap, enum typec_role role) |
|---|
| 357 | +static int tps6598x_pr_set(struct typec_port *port, enum typec_role role) |
|---|
| 327 | 358 | { |
|---|
| 328 | | - struct tps6598x *tps = container_of(cap, struct tps6598x, typec_cap); |
|---|
| 329 | 359 | const char *cmd = (role == TYPEC_SINK) ? "SWSk" : "SWSr"; |
|---|
| 360 | + struct tps6598x *tps = typec_get_drvdata(port); |
|---|
| 330 | 361 | u32 status; |
|---|
| 331 | 362 | int ret; |
|---|
| 332 | 363 | |
|---|
| .. | .. |
|---|
| 352 | 383 | |
|---|
| 353 | 384 | return ret; |
|---|
| 354 | 385 | } |
|---|
| 386 | + |
|---|
| 387 | +static const struct typec_operations tps6598x_ops = { |
|---|
| 388 | + .dr_set = tps6598x_dr_set, |
|---|
| 389 | + .pr_set = tps6598x_pr_set, |
|---|
| 390 | +}; |
|---|
| 355 | 391 | |
|---|
| 356 | 392 | static irqreturn_t tps6598x_interrupt(int irq, void *data) |
|---|
| 357 | 393 | { |
|---|
| .. | .. |
|---|
| 398 | 434 | return IRQ_HANDLED; |
|---|
| 399 | 435 | } |
|---|
| 400 | 436 | |
|---|
| 437 | +static int tps6598x_check_mode(struct tps6598x *tps) |
|---|
| 438 | +{ |
|---|
| 439 | + char mode[5] = { }; |
|---|
| 440 | + int ret; |
|---|
| 441 | + |
|---|
| 442 | + ret = tps6598x_read32(tps, TPS_REG_MODE, (void *)mode); |
|---|
| 443 | + if (ret) |
|---|
| 444 | + return ret; |
|---|
| 445 | + |
|---|
| 446 | + switch (match_string(modes, ARRAY_SIZE(modes), mode)) { |
|---|
| 447 | + case TPS_MODE_APP: |
|---|
| 448 | + return 0; |
|---|
| 449 | + case TPS_MODE_BOOT: |
|---|
| 450 | + dev_warn(tps->dev, "dead-battery condition\n"); |
|---|
| 451 | + return 0; |
|---|
| 452 | + case TPS_MODE_BIST: |
|---|
| 453 | + case TPS_MODE_DISC: |
|---|
| 454 | + default: |
|---|
| 455 | + dev_err(tps->dev, "controller in unsupported mode \"%s\"\n", |
|---|
| 456 | + mode); |
|---|
| 457 | + break; |
|---|
| 458 | + } |
|---|
| 459 | + |
|---|
| 460 | + return -ENODEV; |
|---|
| 461 | +} |
|---|
| 462 | + |
|---|
| 401 | 463 | static const struct regmap_config tps6598x_regmap_config = { |
|---|
| 402 | 464 | .reg_bits = 8, |
|---|
| 403 | 465 | .val_bits = 8, |
|---|
| .. | .. |
|---|
| 406 | 468 | |
|---|
| 407 | 469 | static int tps6598x_probe(struct i2c_client *client) |
|---|
| 408 | 470 | { |
|---|
| 471 | + struct typec_capability typec_cap = { }; |
|---|
| 409 | 472 | struct tps6598x *tps; |
|---|
| 473 | + struct fwnode_handle *fwnode; |
|---|
| 410 | 474 | u32 status; |
|---|
| 411 | 475 | u32 conf; |
|---|
| 412 | 476 | u32 vid; |
|---|
| .. | .. |
|---|
| 423 | 487 | if (IS_ERR(tps->regmap)) |
|---|
| 424 | 488 | return PTR_ERR(tps->regmap); |
|---|
| 425 | 489 | |
|---|
| 426 | | - ret = tps6598x_read32(tps, 0, &vid); |
|---|
| 427 | | - if (ret < 0) |
|---|
| 428 | | - return ret; |
|---|
| 429 | | - if (!vid) |
|---|
| 490 | + ret = tps6598x_read32(tps, TPS_REG_VID, &vid); |
|---|
| 491 | + if (ret < 0 || !vid) |
|---|
| 430 | 492 | return -ENODEV; |
|---|
| 431 | 493 | |
|---|
| 432 | 494 | /* |
|---|
| .. | .. |
|---|
| 439 | 501 | if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) |
|---|
| 440 | 502 | tps->i2c_protocol = true; |
|---|
| 441 | 503 | |
|---|
| 504 | + /* Make sure the controller has application firmware running */ |
|---|
| 505 | + ret = tps6598x_check_mode(tps); |
|---|
| 506 | + if (ret) |
|---|
| 507 | + return ret; |
|---|
| 508 | + |
|---|
| 442 | 509 | ret = tps6598x_read32(tps, TPS_REG_STATUS, &status); |
|---|
| 443 | 510 | if (ret < 0) |
|---|
| 444 | 511 | return ret; |
|---|
| .. | .. |
|---|
| 447 | 514 | if (ret < 0) |
|---|
| 448 | 515 | return ret; |
|---|
| 449 | 516 | |
|---|
| 450 | | - tps->typec_cap.revision = USB_TYPEC_REV_1_2; |
|---|
| 451 | | - tps->typec_cap.pd_revision = 0x200; |
|---|
| 452 | | - tps->typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; |
|---|
| 453 | | - tps->typec_cap.pr_set = tps6598x_pr_set; |
|---|
| 454 | | - tps->typec_cap.dr_set = tps6598x_dr_set; |
|---|
| 517 | + fwnode = device_get_named_child_node(&client->dev, "connector"); |
|---|
| 518 | + if (!fwnode) |
|---|
| 519 | + return -ENODEV; |
|---|
| 520 | + |
|---|
| 521 | + tps->role_sw = fwnode_usb_role_switch_get(fwnode); |
|---|
| 522 | + if (IS_ERR(tps->role_sw)) { |
|---|
| 523 | + ret = PTR_ERR(tps->role_sw); |
|---|
| 524 | + goto err_fwnode_put; |
|---|
| 525 | + } |
|---|
| 526 | + |
|---|
| 527 | + typec_cap.revision = USB_TYPEC_REV_1_2; |
|---|
| 528 | + typec_cap.pd_revision = 0x200; |
|---|
| 529 | + typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; |
|---|
| 530 | + typec_cap.driver_data = tps; |
|---|
| 531 | + typec_cap.ops = &tps6598x_ops; |
|---|
| 532 | + typec_cap.fwnode = fwnode; |
|---|
| 455 | 533 | |
|---|
| 456 | 534 | switch (TPS_SYSCONF_PORTINFO(conf)) { |
|---|
| 457 | 535 | case TPS_PORTINFO_SINK_ACCESSORY: |
|---|
| 458 | 536 | case TPS_PORTINFO_SINK: |
|---|
| 459 | | - tps->typec_cap.type = TYPEC_PORT_SNK; |
|---|
| 460 | | - tps->typec_cap.data = TYPEC_PORT_UFP; |
|---|
| 537 | + typec_cap.type = TYPEC_PORT_SNK; |
|---|
| 538 | + typec_cap.data = TYPEC_PORT_UFP; |
|---|
| 461 | 539 | break; |
|---|
| 462 | 540 | case TPS_PORTINFO_DRP_UFP_DRD: |
|---|
| 463 | 541 | case TPS_PORTINFO_DRP_DFP_DRD: |
|---|
| 464 | | - tps->typec_cap.type = TYPEC_PORT_DRP; |
|---|
| 465 | | - tps->typec_cap.data = TYPEC_PORT_DRD; |
|---|
| 542 | + typec_cap.type = TYPEC_PORT_DRP; |
|---|
| 543 | + typec_cap.data = TYPEC_PORT_DRD; |
|---|
| 466 | 544 | break; |
|---|
| 467 | 545 | case TPS_PORTINFO_DRP_UFP: |
|---|
| 468 | | - tps->typec_cap.type = TYPEC_PORT_DRP; |
|---|
| 469 | | - tps->typec_cap.data = TYPEC_PORT_UFP; |
|---|
| 546 | + typec_cap.type = TYPEC_PORT_DRP; |
|---|
| 547 | + typec_cap.data = TYPEC_PORT_UFP; |
|---|
| 470 | 548 | break; |
|---|
| 471 | 549 | case TPS_PORTINFO_DRP_DFP: |
|---|
| 472 | | - tps->typec_cap.type = TYPEC_PORT_DRP; |
|---|
| 473 | | - tps->typec_cap.data = TYPEC_PORT_DFP; |
|---|
| 550 | + typec_cap.type = TYPEC_PORT_DRP; |
|---|
| 551 | + typec_cap.data = TYPEC_PORT_DFP; |
|---|
| 474 | 552 | break; |
|---|
| 475 | 553 | case TPS_PORTINFO_SOURCE: |
|---|
| 476 | | - tps->typec_cap.type = TYPEC_PORT_SRC; |
|---|
| 477 | | - tps->typec_cap.data = TYPEC_PORT_DFP; |
|---|
| 554 | + typec_cap.type = TYPEC_PORT_SRC; |
|---|
| 555 | + typec_cap.data = TYPEC_PORT_DFP; |
|---|
| 478 | 556 | break; |
|---|
| 479 | 557 | default: |
|---|
| 480 | | - return -ENODEV; |
|---|
| 558 | + ret = -ENODEV; |
|---|
| 559 | + goto err_role_put; |
|---|
| 481 | 560 | } |
|---|
| 482 | 561 | |
|---|
| 483 | | - tps->port = typec_register_port(&client->dev, &tps->typec_cap); |
|---|
| 484 | | - if (IS_ERR(tps->port)) |
|---|
| 485 | | - return PTR_ERR(tps->port); |
|---|
| 562 | + tps->port = typec_register_port(&client->dev, &typec_cap); |
|---|
| 563 | + if (IS_ERR(tps->port)) { |
|---|
| 564 | + ret = PTR_ERR(tps->port); |
|---|
| 565 | + goto err_role_put; |
|---|
| 566 | + } |
|---|
| 567 | + fwnode_handle_put(fwnode); |
|---|
| 486 | 568 | |
|---|
| 487 | 569 | if (status & TPS_STATUS_PLUG_PRESENT) { |
|---|
| 488 | 570 | ret = tps6598x_connect(tps, status); |
|---|
| .. | .. |
|---|
| 497 | 579 | if (ret) { |
|---|
| 498 | 580 | tps6598x_disconnect(tps, 0); |
|---|
| 499 | 581 | typec_unregister_port(tps->port); |
|---|
| 500 | | - return ret; |
|---|
| 582 | + goto err_role_put; |
|---|
| 501 | 583 | } |
|---|
| 502 | 584 | |
|---|
| 503 | 585 | i2c_set_clientdata(client, tps); |
|---|
| 504 | 586 | |
|---|
| 505 | 587 | return 0; |
|---|
| 588 | + |
|---|
| 589 | +err_role_put: |
|---|
| 590 | + usb_role_switch_put(tps->role_sw); |
|---|
| 591 | +err_fwnode_put: |
|---|
| 592 | + fwnode_handle_put(fwnode); |
|---|
| 593 | + |
|---|
| 594 | + return ret; |
|---|
| 506 | 595 | } |
|---|
| 507 | 596 | |
|---|
| 508 | 597 | static int tps6598x_remove(struct i2c_client *client) |
|---|
| .. | .. |
|---|
| 511 | 600 | |
|---|
| 512 | 601 | tps6598x_disconnect(tps, 0); |
|---|
| 513 | 602 | typec_unregister_port(tps->port); |
|---|
| 603 | + usb_role_switch_put(tps->role_sw); |
|---|
| 514 | 604 | |
|---|
| 515 | 605 | return 0; |
|---|
| 516 | 606 | } |
|---|
| 517 | 607 | |
|---|
| 518 | | -static const struct acpi_device_id tps6598x_acpi_match[] = { |
|---|
| 519 | | - { "INT3515", 0 }, |
|---|
| 608 | +static const struct of_device_id tps6598x_of_match[] = { |
|---|
| 609 | + { .compatible = "ti,tps6598x", }, |
|---|
| 610 | + {} |
|---|
| 611 | +}; |
|---|
| 612 | +MODULE_DEVICE_TABLE(of, tps6598x_of_match); |
|---|
| 613 | + |
|---|
| 614 | +static const struct i2c_device_id tps6598x_id[] = { |
|---|
| 615 | + { "tps6598x" }, |
|---|
| 520 | 616 | { } |
|---|
| 521 | 617 | }; |
|---|
| 522 | | -MODULE_DEVICE_TABLE(acpi, tps6598x_acpi_match); |
|---|
| 618 | +MODULE_DEVICE_TABLE(i2c, tps6598x_id); |
|---|
| 523 | 619 | |
|---|
| 524 | 620 | static struct i2c_driver tps6598x_i2c_driver = { |
|---|
| 525 | 621 | .driver = { |
|---|
| 526 | 622 | .name = "tps6598x", |
|---|
| 527 | | - .acpi_match_table = tps6598x_acpi_match, |
|---|
| 623 | + .of_match_table = tps6598x_of_match, |
|---|
| 528 | 624 | }, |
|---|
| 529 | 625 | .probe_new = tps6598x_probe, |
|---|
| 530 | 626 | .remove = tps6598x_remove, |
|---|
| 627 | + .id_table = tps6598x_id, |
|---|
| 531 | 628 | }; |
|---|
| 532 | 629 | module_i2c_driver(tps6598x_i2c_driver); |
|---|
| 533 | 630 | |
|---|